Doubly Linked List in Python
python algorithmAs a hacker, it is a shame of being amazed by the magic without digging into
the prestige. So I checkout the implementation of
OrderedDict, and
found how OrderedDict handles the insertion order internally: a doubly linked
list to track the insertion order, with a dict mapping the key to the node for
quick access.
It is worthy noting that the doubly linked list is implemented in a bizarre and elegant way, here is a stripped version for the sake of simplicity:
def dll_init():
    head = []
    head[:] = [head, head, None]
    return head
def dll_append(head, value):
    last = head[0]
    node = [last, head, value]
    last[1] = head[0] = node
    return node
def dll_remove(node):
    prev_link, next_link, _ = node
    prev_link[1] = next_link
    next_link[0] = prev_link
    return node
def dll_insert_before(succedent, value):
    # Create a node to host value, and insert BEFORE the succedent
    node = [succedent[0], succedent, value]
    succedent[0][1] = node
    succedent[0] = node
    return node
def dll_insert_after(precedent, value):
    # Create a node to host value, and insert AFTER the precedent
    node = [precedent, precedent[1], value]
    precedent[1][0] = node
    precedent[1] = node
    return node
def dll_iter(head):
    curr = head[1]              # start at the first node
    while curr is not head:
        yield curr[2]           # yield the curr[KEY]
        curr = curr[1]          # move to next node
Each node of the doubly linked list is a tuple of
(next_node, previous_node, value). The most intriguing part is
head[:] = [head, head, None]: we create a sentinel by setting the head’s
next_node, previous_node pointing to itself. If the list is not empty, the
head’s previous_node will point to the last node, tail; and the tail’s
next_node will point to the head. The rest of the code is just self-explained.
Let’s play it in the sandbox:
In [42]: head = dll_init()
In [43]: dll_append(head, 1)
Out[43]: [[[...], [...], None], [[...], [...], None], 1]
In [44]: dll_append(head, 2)
Out[44]: [[[[...], [...], None], [...], 1], [[...], [[...], [...], 1], None], 2]
In [45]: dll_append(head, 3)
Out[45]:
[[[[[...], [...], None], [...], 1], [...], 2],
 [[...], [[...], [[...], [...], 2], 1], None],
 3]
In [46]: list(dll_iter(head))
Out[46]: [1, 2, 3]
In [47]: head[1] == head
---------------------------------------------------------------------------
RecursionError                            Traceback (most recent call last)
<ipython-input-47-c6cdf1199c54> in <module>()
----> 1 head[1] == head
RecursionError: maximum recursion depth exceeded in comparison
In [48]: head[1] is head
Out[48]: False
In [49]: id(head[1]), id(head)
Out[49]: (4359947336, 4359845960)
The value comparison operator == will not work as the nodes are compared
lexicographically and recursively.
Sequences compare lexicographically using comparison of corresponding elements, whereby reflexivity of the elements is enforced.
We should use the identity comparison, is or compare the value of id as
described above, see the
language specification
for more details.