11. XOR linked list
This is your coding interview problem for today.
This problem was asked by Google.
An XOR linked list is a more memory efficient doubly linked list. Instead of each node holding next
and prev
fields, it holds a field named both
, which is an XOR of the next node and the previous node. Implement an XOR linked list; it has an add(element)
which adds the element to the end, and a get(index)
which returns the node at index.
If using a language that has no pointers (such as Python), you can assume you have access to get_pointer
and dereference_pointer
functions that converts between nodes and memory addresses.
For the head, both
will just be the address of next, and if it's the tail, it should just be the address of prev. And intermediate nodes should have an XOR of next
and prev
.
Here's an example XOR linked list which meets the above conditions:
A <-> B <-> C <-> D
B A ⊕ C B ⊕ D C
Let's work through get
first, assuming that the above conditions are maintained. Then, given a node, to go to the next node, we have to XOR the current node's both
with the previous node's address. And to handle getting the next node from the head, we would initialize the previous node's address as 0.
So in the above example, A
's both
is B
which when XOR'd with 0
would become B
. Then B
's both
is A ⊕ C
, which when XOR'd with A
becomes C, etc.
To implement add
, we would need to update current tail's both
to be XOR'd by its current both
the new node's memory address. Then the new node's both
would just point to the memory address of the current tail. Finally, we'd update the current tail to be equal to the new node.
import ctypes
# This is hacky. It's a data structure for C, not python.
class Node(object):
def __init__(self, val):
self.val = val
self.both = 0
class XorLinkedList(object):
def __init__(self):
self.head = self.tail = None
self.__nodes = [] # This is to prevent garbage collection
def add(self, node):
if self.head is None:
self.head = self.tail = node
else:
self.tail.both = id(node) ^ self.tail.both
node.both = id(self.tail)
self.tail = node
# Without this line, Python thinks there is no way to reach nodes between
# head and tail.
self.__nodes.append(node)
def get(self, index):
prev_id = 0
node = self.head
for i in range(index):
next_id = prev_id ^ node.both
if next_id:
prev_id = id(node)
node = _get_obj(next_id)
else:
raise IndexError('Linked list index out of range')
return node
def _get_obj(id):
return ctypes.cast(id, ctypes.py_object).value
add
runs in O(1) time and get
runs in O(N) time.
Last updated
Was this helpful?