XOR Linked List – A Memory Efficient Doubly Linked List | Set 1
An ordinary Doubly Linked List requires space for two address fields to store the addresses of previous and next nodes. It is represented as follows in the image below. From the below image, it can be depicted out that the address of the previous node is retained and carried over for computation by the previous pointer while that of the next node is after pointers similarly.
Now there is a memory-efficient version of Doubly Linked List that can be created using only one space for the address field with every node. This memory efficient Doubly Linked List is called XOR Linked List or Memory Efficient as the list uses bitwise XOR operation to save space for one address. In the XOR linked list, instead of storing actual memory addresses, every node stores the XOR of addresses of previous and next nodes.
Consider the above Doubly Linked List. Following are the Ordinary and XOR (or Memory Efficient) representations of the Doubly Linked List.
Now here we will be discussing out both the ways in order to perch out how XOR representation behaves differently from ordinary representation.
- Ordinary Representation
- XOR List Representation
Way 1: Ordinary Representation
prev = NULL, next = add(B) // previous is NULL and next is address of B
prev = add(A), next = add(C) // previous is address of A and next is address of C
prev = add(B), next = add(D) // previous is address of B and next is address of D
prev = add(C), next = NULL // previous is address of C and next is NULL
Way 2: XOR List Representation
Let us call the address variable in XOR representation npx (XOR of next and previous)
While traversing XOR Linked List we can traverse the XOR list in both forward and reverse directions. While traversing the list we need to remember the address of the previously accessed node in order to calculate the next node’s address.
For example: When we are at node C, we must have the address of B. XOR of add(B) and npx of C gives us the add(D).
npx = 0 XOR add(B) // bitwise XOR of zero and address of B
npx = add(A) XOR add(C) // bitwise XOR of address of A and address of C
npx = add(B) XOR add(D) // bitwise XOR of address of B and address of D
npx = add(C) XOR 0 // bitwise XOR of address of C and 0
npx(C) XOR add(B) => (add(B) XOR add(D)) XOR add(B) // npx(C) = add(B) XOR add(D) => add(B) XOR add(D) XOR add(B) // a^b = b^a and (a^b)^c = a^(b^c) => add(D) XOR 0 // a^a = 0 => add(D) // a^0 = a
Similarly, we can traverse the list in the backward direction. Now straightaway coming down to the implementation part in order to figure out better.
Below is the implementation of the above approach:
The nodes of Linked List are: 10000 1000 100 10
Time Complexity: O(n)
Auxiliary Space: O(1)