Skip to content

Commit 08fd580

Browse files
committed
Add XOR Linked List implementation with doctests
1 parent 6956497 commit 08fd580

File tree

1 file changed

+29
-68
lines changed

1 file changed

+29
-68
lines changed

data_structures/linked_list/xor_linked_list.py

Lines changed: 29 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -11,107 +11,68 @@
1111
[10, 20, 30]
1212
"""
1313

14-
# Note: This implementation simulates pointer behavior using Python's id()
15-
# and a dictionary lookup (_nodes). In languages like C, this would
16-
# be done with actual memory addresses and pointer arithmetic.
14+
# Note: 'from typing import Optional' is no longer needed
15+
# as we use the modern 'Node | None' syntax.
1716

1817

1918
class Node:
20-
def __init__(self, value: int):
19+
def __init__(self, value: int) -> None:
20+
"""Initializes a Node with a value and a null pointer."""
2121
self.value = value
2222
self.both: int = 0 # XOR of prev and next node ids
2323

2424

2525
class XORLinkedList:
26-
def __init__(self):
27-
# Use 'Node | None' instead of 'Optional[Node]' (per ruff UP045)
26+
def __init__(self) -> None:
27+
"""Initializes an empty XOR Linked List."""
2828
self.head: Node | None = None
2929
self.tail: Node | None = None
3030
# id -> node map to simulate pointer references
31-
# In a low-level language, we would just store/use the memory addresses.
3231
self._nodes: dict[int, Node] = {}
3332

34-
def _get_node(self, node_id: int) -> Node | None:
35-
"""Helper to retrieve a node by its simulated ID."""
36-
return self._nodes.get(node_id)
37-
38-
def _get_id(self, node: Node | None) -> int:
39-
"""Helper to get the simulated ID (address) of a node."""
40-
return id(node) if node else 0
41-
4233
def _xor(self, a: Node | None, b: Node | None) -> int:
43-
"""Helper to XOR the IDs (addresses) of two nodes."""
44-
# Use 'Node | None' instead of 'Optional[Node]' (per ruff UP045)
45-
return self._get_id(a) ^ self._get_id(b)
34+
"""Helper function to get the XOR of two node IDs."""
35+
return (id(a) if a else 0) ^ (id(b) if b else 0)
4636

4737
def insert(self, value: int) -> None:
4838
"""Inserts a value at the end of the list."""
4939
node = Node(value)
50-
# Store the node in our simulated memory
5140
self._nodes[id(node)] = node
5241

5342
if self.head is None:
54-
# List is empty
55-
self.head = node
56-
self.tail = node
57-
# node.both remains 0 (XOR of None and None)
43+
# If the list is empty, head and tail are the new node
44+
self.head = self.tail = node
5845
else:
59-
# List is not empty, append to tail
60-
# New node's 'both' points back to the old tail
61-
node.both = self._get_id(self.tail)
62-
63-
# Update the old tail's 'both'
64-
# old_tail.both = (ID of node before old_tail) ^ (ID of new node)
65-
# We can get (ID of node before old_tail) by doing:
66-
# old_tail.both ^ (ID of next node, which was None/0)
67-
# So, old_tail.both ^ 0 = old_tail.both
68-
# new_tail.both = old_tail.both ^ self._get_id(node)
69-
70-
# Simplified:
71-
# self.tail.both was (ID of prev_node) ^ 0
72-
# We need it to be (ID of prev_node) ^ (ID of new node)
73-
# So we XOR it with the new node's ID.
74-
if self.tail: # Type checker guard
75-
self.tail.both = self.tail.both ^ self._get_id(node)
76-
77-
# Update the tail pointer
46+
# If the list is not empty, append to the tail
47+
# The new node's pointer is just the ID of the old tail
48+
node.both = id(self.tail)
49+
if self.tail: # Type checker guard
50+
# The old tail's pointer must be updated to XOR
51+
# its previous node ID with the new node's ID.
52+
# self.tail.both was (prev_id ^ 0)
53+
# self.tail.both becomes (prev_id ^ new_node_id)
54+
self.tail.both ^= id(node)
7855
self.tail = node
7956

8057
def to_list(self) -> list[int]:
81-
"""Converts the XOR linked list to a standard Python list (forward)."""
58+
"""Converts the XOR list to a standard Python list (forward traversal)."""
8259
result = []
60+
prev_id = 0
8361
current = self.head
84-
prev_id = 0 # ID of the virtual 'None' node before head
85-
8662
while current:
8763
result.append(current.value)
88-
89-
# Get the ID of the next node
90-
# current.both = (ID of prev_node) ^ (ID of next_node)
91-
# So, (ID of next_node) = (ID of prev_node) ^ current.both
64+
# Find next node's ID:
65+
# current.both = prev_id ^ next_id
66+
# so, next_id = prev_id ^ current.both
9267
next_id = prev_id ^ current.both
93-
94-
# Move to the next node
95-
prev_id = self._get_id(current)
96-
current = self._get_node(next_id)
97-
68+
69+
# Move forward
70+
prev_id = id(current)
71+
current = self._nodes.get(next_id)
9872
return result
9973

10074

10175
if __name__ == "__main__":
10276
import doctest
10377

104-
doctest.testmod()
105-
106-
# Additional demonstration
107-
xor_list = XORLinkedList()
108-
xor_list.insert(10)
109-
xor_list.insert(20)
110-
xor_list.insert(30)
111-
print(f"List contents: {xor_list.to_list()}") # Output: [10, 20, 30]
112-
113-
# Verify head and tail
114-
if xor_list.head:
115-
print(f"Head value: {xor_list.head.value}") # Output: 10
116-
if xor_list.tail:
117-
print(f"Tail value: {xor_list.tail.value}") # Output: 30
78+
doctest.testmod()

0 commit comments

Comments
 (0)