Skip to content

Commit 6956497

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

File tree

1 file changed

+74
-16
lines changed

1 file changed

+74
-16
lines changed
Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
"""
22
XOR Linked List implementation
3-
43
A memory-efficient doubly linked list using XOR of node addresses.
54
Each node stores one pointer that is the XOR of previous and next node addresses.
6-
75
Example:
86
>>> xor_list = XORLinkedList()
97
>>> xor_list.insert(10)
@@ -13,47 +11,107 @@
1311
[10, 20, 30]
1412
"""
1513

16-
from typing import Optional
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.
1717

1818

1919
class Node:
20-
def __init__(self, value: int) -> None:
20+
def __init__(self, value: int):
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) -> None:
27-
self.head: Optional[Node] = None
28-
self.tail: Optional[Node] = None
29-
self._nodes = {} # id → node map to simulate pointer references
26+
def __init__(self):
27+
# Use 'Node | None' instead of 'Optional[Node]' (per ruff UP045)
28+
self.head: Node | None = None
29+
self.tail: Node | None = None
30+
# id -> node map to simulate pointer references
31+
# In a low-level language, we would just store/use the memory addresses.
32+
self._nodes: dict[int, Node] = {}
3033

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)
3137

32-
def _xor(self, a: Optional[Node], b: Optional[Node]) -> int:
33-
return (id(a) if a else 0) ^ (id(b) if b else 0)
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+
42+
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)
3446

3547
def insert(self, value: int) -> None:
48+
"""Inserts a value at the end of the list."""
3649
node = Node(value)
50+
# Store the node in our simulated memory
3751
self._nodes[id(node)] = node
52+
3853
if self.head is None:
39-
self.head = self.tail = node
54+
# List is empty
55+
self.head = node
56+
self.tail = node
57+
# node.both remains 0 (XOR of None and None)
4058
else:
41-
node.both = id(self.tail)
42-
self.tail.both ^= id(node)
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
4378
self.tail = node
4479

4580
def to_list(self) -> list[int]:
81+
"""Converts the XOR linked list to a standard Python list (forward)."""
4682
result = []
47-
prev_id = 0
4883
current = self.head
84+
prev_id = 0 # ID of the virtual 'None' node before head
85+
4986
while current:
5087
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
5192
next_id = prev_id ^ current.both
52-
prev_id = id(current)
53-
current = self._nodes.get(next_id)
93+
94+
# Move to the next node
95+
prev_id = self._get_id(current)
96+
current = self._get_node(next_id)
97+
5498
return result
5599

56100

57101
if __name__ == "__main__":
58102
import doctest
103+
59104
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

0 commit comments

Comments
 (0)