XOR Linked List – A Memory Efficient Doubly Linked List | Set 1
In this post, we’re going to talk about how XOR linked lists are used to reduce the memory requirements of doubly-linked lists.
We know that each node in a doubly-linked list has two pointer fields which contain the addresses of the previous and next node. On the other hand, each node of the XOR linked list requires only a single pointer field, which doesn’t store the actual memory addresses but stores the bitwise XOR of addresses for its previous and next node.

XOR Linked List
Following are the Ordinary and XOR (or Memory Efficient) representations of the Doubly Linked List:

XOR Linked List Representation.
In this section, we will discuss both ways in order to demonstrate how XOR representation of doubly linked list differs from ordinary representation of doubly linked list.
- Ordinary Representation
- XOR List Representation
Ordinary Representation of doubly linked list.
Node A:
prev = NULL, next = add(B) // previous is NULL and next is address of BNode B:
prev = add(A), next = add(C) // previous is address of A and next is address of CNode C:
prev = add(B), next = add(D) // previous is address of B and next is address of DNode D:
prev = add(C), next = NULL // previous is address of C and next is NULL
XOR List Representation of doubly linked list.
Lets see the structure of each node of Doubly linked list and XOR linked list:
Below is the representation of a node structure for an XOR linked list:
struct Node {
int data;
// "both": XOR of the previous and next node addresses
Node* both;
};
class Node {
int data;
Node both; // XOR of the previous and next node addresses
}
class Node:
def __init__(self, data):
self.data = data # Data stored in the node
self.prev = None # Reference to the previous node
self.next = None # Reference to the next node
class DoublyLinkedList:
def __init__(self):
self.head = None # Reference to the first node
self.tail = None # Reference to the last node
# Other methods of Doubly Linked List can be implemented here
class Node {
constructor(data) {
this.data = data; // Data stored in the node
this.both = null; // XOR of the previous and next node addresses
}
}
// In JavaScript, there's no native XOR operation on memory addresses like in C/C++
// You can simulate similar behavior using references or pointers to nodes
// However, JavaScript does not provide direct memory manipulation, so XOR operation on addresses is not feasible
// Instead, you can simply store references to the previous and next nodes directly
// For example:
class DoublyLinkedList {
constructor() {
this.head = null; // Reference to the first node
this.tail = null; // Reference to the last node
}
// Other methods of Doubly Linked List can be implemented here
}
Types of XOR Linked List:
There are two main types of XOR Linked List:
- Singly Linked XOR List: A singly XOR linked list is a variation of the XOR linked list that uses the XOR operation to store the memory address of the next node in a singly linked list. In this type of list, each node stores the XOR of the memory address of the next node and the memory address of the current node.
- Doubly Linked XOR List: A doubly XOR linked list is a variation of the XOR linked list that uses the XOR operation to store the memory addresses of the next and previous nodes in a doubly linked list. In this type of list, each node stores the XOR of the memory addresses of the next and previous nodes.
Traversal in XOR linked list:
Two types of traversal are possible in XOR linked list.
- Forward Traversal
- Backward Traversal:
Forward Traversal in XOR linked list:
When traversing the list forward, it’s important to always keep the memory address of the previous element. Address of previous element helps in calculating the address of the next element by the below formula:
address of next Node = (address of prev Node) ^ (both)
Here, “both” is the XOR of address of previous node and address of next node.
.png)
Forward Traversal of XOR Linked List
Below is the code snippet for forward traversal of the XOR linked list:
Node* prev;
// Curr points to the first node
// of the XOR Linked list
Node* curr = head;
Node* next;
While(curr != NULL)
{
cout << curr->data;
// both represents the XOR value .
next = prev ^ curr->both;
prev = curr;
curr = next;
}
// Assuming Node is a class representing a node in the XOR
// Linked list with appropriate properties and methods.
Node prev = null;
// Curr points to the first node
// of the XOR Linked list
Node curr = head;
Node next;
while (curr != null) {
System.out.print(curr.data);
// both represents the XOR value .
next = prev ^ curr.both;
prev = curr;
curr = next;
}
// This code is contributed by Susobhan Akhuli
prev = None
# Curr points to the first node
# of the XOR Linked list
curr = head
while curr is not None:
print(curr.data, end=" ")
# "both" represents the XOR value.
next_node = prev ^ curr.both
prev = curr
curr = next_node
let prev;
// Curr points to the first node
// of the XOR Linked list
let curr = head;
let next;
while (curr !== null)
{
console.log(curr.data);
// both represents the XOR value .
next = prev ^ curr.both;
prev = curr;
curr = next;
}
// This code is contributed by Susobhan Akhuli
Backward Traversal in XOR linked list:
When traversing the list backward, it’s important to always keep the memory address of the next element. Address of next element helps in calculating the address of the previous element by the below formula:
address of previous Node = (address of next Node) ^ (both)
Here, “both” is the XOR of address of previous node and address of next node.
.png)
Backward Traversal of XOR Linked List
Below is the code snippet for backward traversal of the XOR linked list:
// Curr points to the last node
//of the XOR Linked list
Node * curr ;
Node *head;
Node *prev, *next=NULL;
while(curr!=NULL)
{
cout<<curr->data;
//both represents the XOR value of the node.
prev= (next) ^ (curr->both);
next = curr;
curr = prev;
}
// Curr points to the last node
//of the XOR Linked list
Node curr;
Node head;
Node prev, next = null;
while (curr != null) {
System.out.println(curr.data);
//both represents the XOR value of the node.
prev = (next) ^ (curr.both);
next = curr;
curr = prev;
}
import ctypes
class Node:
def __init__(self, data):
self.data = data
self.both = None
def XOR(a, b):
return ctypes.cast(ctypes.pointer(ctypes.c_int(a)), ctypes.POINTER(ctypes.c_int)).value ^ \
ctypes.cast(ctypes.pointer(ctypes.c_int(b)), ctypes.POINTER(ctypes.c_int)).value
def main():
# Curr points to the last node
curr = None
head = None
prev = next_node = None
while curr is not None:
print(curr.data),
# both represents the XOR value of the node.
prev = XOR(next_node, curr.both)
next_node = curr
curr = prev
if __name__ == "__main__":
main()
using System;
using System.Runtime.InteropServices;
class Program
{
// Node class definition
class Node
{
public int data;
public Node both;
}
static unsafe void Main()
{
// Curr points to the last node
Node curr = null;
Node head = null;
Node prev, next = null;
while (curr != null)
{
Console.Write(curr.data + " ");
// both represents the XOR value of the node.
prev = XOR(next, curr.both);
next = curr;
curr = prev;
}
}
// Helper method for XOR operation
static Node XOR(Node a, Node b)
{
return (Node)((IntPtr)a ^ (IntPtr)b);
}
}
class Node {
constructor(data) {
this.data = data;
this.both = null;
}
}
let curr;
let head;
let prev, next = null;
while (curr !== null) {
console.log(curr.data);
// both represents the XOR value of the node.
prev = next ^ curr.both;
next = curr;
curr = prev;
}
Basic Operations of XOR Linked list:
- Insertion
- Deletion
Insertion at Beginning in XOR Linked List:
Below is the steps for insert an element at beginning in XOR Linked List:
- Create a new node , initialize the data and address to the (NULL ^ address of head)
- Then check, If the list is empty, return with that node;
- Otherwise, assign the XOR of the head node to the XOR(new_node address, XOR(head->both, nullptr))
Insertion at end in XOR Linked List:
Below is the steps for insert an element at end in XOR Linked List:
- Create a new node , initialize the data and address to the (NULL ^ add. of tail)
- Then check, If the list is empty, return with that node;
- Otherwise, assign the XOR of the tail node to the XOR(XOR(tail->both, nullptr), new_node address)
Deletion at Beginning in XOR Linked List:
Below is the steps for delete an element at beginning in XOR Linked List:
- Check if the head pointer is not null (i.e., the list is not empty).
- Find the next node’s address using XOR by performing XOR(head->both, nullptr)
- Delete the current head node to free up the memory.and Update the head pointer to point to the calculated next node.
Deletion at End in XOR Linked List:
Below is the steps for delete an element at beginning in XOR Linked List:
- Check if the tail pointer is not null (i.e., the list is not empty).
- If the list is not empty:
- Find the previous node’s address using XOR by performing XOR(tail->both, nullptr). This gives you the previous node in the list.
- Delete the current tail node to free up the memory.
- Update the tail pointer to point to the calculated previous node.
Below is the implementation of the above approach:
// C++ implementation of the above approach
#include <cstdint>
#include <iostream>
struct Node {
int data;
// XOR of next and prev
Node* both;
};
// Class representing XOR linked list
class XORLinkedList {
private:
Node* head;
Node* tail;
// XOR function for Node pointers
Node* XOR(Node* a, Node* b);
public:
// Constructor to initialize
// an empty list
XORLinkedList();
// Insert a node at the head of the list
void insertAtHead(int data);
// Insert a node at the tail of the list
void insertAtTail(int data);
// Delete a node from the head
// of the list
void deleteFromHead();
// Delete a node from the tail
// of the list
void deleteFromTail();
// Print the elements of the list
void printList();
};
XORLinkedList::XORLinkedList() {
// Initialize head and tail to
// nullptr for an empty list
head = tail = nullptr;
}
Node* XORLinkedList::XOR(Node* a, Node* b) {
// XOR operation for Node pointers
return (Node*)((uintptr_t)(a) ^ (uintptr_t)(b));
}
void XORLinkedList::insertAtHead(int data) {
Node* newNode = new Node();
newNode->data = data;
newNode->both = head;
if (head)
head->both = XOR(newNode, XOR(head->both, nullptr));
else {
// If the list was empty, the new
// node is both the head and the tail
tail = newNode;
}
// Update the head to the new node
head = newNode;
}
void XORLinkedList::insertAtTail(int data) {
Node* newNode = new Node();
newNode->data = data;
newNode->both = tail;
if (tail)
tail->both = XOR(XOR(tail->both, nullptr), newNode);
else {
// If the list was empty, the new
// node is both the head and the tail
head = newNode;
}
// Update the tail to the new node
tail = newNode;
}
void XORLinkedList::deleteFromHead() {
if (!head) return;
Node* temp = head;
Node* next = head->both;
if (next) {
Node* nextNext = XOR(head, next->both);
next->both = nextNext;
}
else {
tail = nullptr;
}
head = next;
delete temp;
}
void XORLinkedList::deleteFromTail() {
if (!tail) return;
Node* temp = tail;
Node* prev = tail->both;
if (prev) {
Node* prevPrev = XOR(tail, prev->both);
prev->both = prevPrev;
}
else {
head = nullptr;
}
tail = prev;
delete temp;
}
void XORLinkedList::printList() {
Node* current = head;
Node* prev = nullptr;
while (current) {
std::cout << current->data << " ";
Node* next = XOR(prev, current->both);
prev = current;
current = next;
}
std::cout << std::endl;
}
int main() {
XORLinkedList list;
list.insertAtHead(10);
list.insertAtHead(20);
list.insertAtTail(30);
list.insertAtTail(40);
// prints 20 10 30 40
list.printList();
list.deleteFromHead();
// prints 10 30 40
list.printList();
list.deleteFromTail();
// prints 10 30
list.printList();
return 0;
}
import java.util.*;
class Node {
int data;
int both; // XOR of previous and next node ids
int id;
static int nextId = 1;
Node(int data) {
this.data = data;
this.both = 0;
this.id = nextId++;
}
}
class GFG {
Node head;
Node tail;
// Global memory map to simulate pointer addresses
static Map<Integer, Node> memory = new HashMap<>();
// XOR function for Node pointers
Node XOR(Node a, Node b) {
int idA = (a == null) ? 0 : a.id;
int idB = (b == null) ? 0 : b.id;
int xorId = idA ^ idB;
return memory.get(xorId);
}
// Constructor to initialize
// an empty list
GFG() {
head = tail = null;
}
// Insert a node at the head of the list
void insertAtHead(int data) {
Node newNode = new Node(data);
memory.put(newNode.id, newNode);
newNode.both = (head == null) ? 0 : head.id;
if (head != null)
head.both = head.both ^ newNode.id;
else {
// If the list was empty, the new
// node is both the head and the tail
tail = newNode;
}
head = newNode;
}
// Insert a node at the tail of the list
void insertAtTail(int data) {
Node newNode = new Node(data);
memory.put(newNode.id, newNode);
newNode.both = (tail == null) ? 0 : tail.id;
if (tail != null)
tail.both = tail.both ^ newNode.id;
else {
// If the list was empty, the new
// node is both the head and the tail
head = newNode;
}
tail = newNode;
}
// Delete a node from the head
// of the list
void deleteFromHead() {
if (head == null)
return;
Node temp = head;
Node next = (head.both == 0) ? null : memory.get(head.both);
if (next != null) {
next.both = next.both ^ head.id;
} else {
tail = null;
}
memory.remove(head.id);
head = next;
}
// Delete a node from the tail
// of the list
void deleteFromTail() {
if (tail == null)
return;
Node temp = tail;
Node prev = (tail.both == 0) ? null : memory.get(tail.both);
if (prev != null) {
prev.both = prev.both ^ tail.id;
} else {
head = null;
}
memory.remove(tail.id);
tail = prev;
}
// Print the elements of the list
void printList() {
Node current = head;
Node prev = null;
while (current != null) {
System.out.print(current.data + " ");
int nextId = current.both ^ (prev == null ? 0 : prev.id);
Node next = memory.get(nextId);
prev = current;
current = next;
}
System.out.println();
}
public static void main(String[] args) {
GFG list = new GFG();
list.insertAtHead(10);
list.insertAtHead(20);
list.insertAtTail(30);
list.insertAtTail(40);
// prints 20 10 30 40
list.printList();
list.deleteFromHead();
// prints 10 30 40
list.printList();
list.deleteFromTail();
// prints 10 30
list.printList();
}
}
# Python program to reverse alternate
# levels of a binary tree
# To simulate an XOR linked list, each node is assigned a unique id,
# and we maintain a global dictionary 'memory' mapping ids to nodes.
memory = {}
class Node:
nextId = 1
def __init__(self, data):
self.data = data
self.both = 0 # XOR of previous and next node ids
self.id = Node.nextId
Node.nextId += 1
memory[self.id] = self
class XORLinkedList:
def __init__(self):
self.head = None
self.tail = None
def XOR(self, a, b):
id_a = a.id if a is not None else 0
id_b = b.id if b is not None else 0
xor_id = id_a ^ id_b
return memory.get(xor_id, None)
# Insert a node at the head of the list
def insertAtHead(self, data):
newNode = Node(data)
newNode.both = self.head.id if self.head is not None else 0
if self.head is not None:
self.head.both = self.head.both ^ newNode.id
else:
self.tail = newNode
self.head = newNode
# Insert a node at the tail of the list
def insertAtTail(self, data):
newNode = Node(data)
newNode.both = self.tail.id if self.tail is not None else 0
if self.tail is not None:
self.tail.both = self.tail.both ^ newNode.id
else:
self.head = newNode
self.tail = newNode
# Delete a node from the head of the list
def deleteFromHead(self):
if self.head is None:
return
temp = self.head
nextNode = memory.get(self.head.both, None) if self.head.both != 0 else None
if nextNode is not None:
nextNode.both = nextNode.both ^ self.head.id
else:
self.tail = None
del memory[self.head.id]
self.head = nextNode
# Delete a node from the tail of the list
def deleteFromTail(self):
if self.tail is None:
return
temp = self.tail
prevNode = memory.get(self.tail.both, None) if self.tail.both != 0 else None
if prevNode is not None:
prevNode.both = prevNode.both ^ self.tail.id
else:
self.head = None
del memory[self.tail.id]
self.tail = prevNode
# Print the elements of the list
def printList(self):
current = self.head
prev = None
while current is not None:
print(current.data, end=" ")
next_id = current.both ^ (prev.id if prev else 0)
prev = current
current = memory.get(next_id, None)
print()
if __name__ == "__main__":
lst = XORLinkedList()
lst.insertAtHead(10)
lst.insertAtHead(20)
lst.insertAtTail(30)
lst.insertAtTail(40)
# prints 20 10 30 40
lst.printList()
lst.deleteFromHead()
# prints 10 30 40
lst.printList()
lst.deleteFromTail()
# prints 10 30
lst.printList()
// C# program to implement XOR Linked List
// to add 1 to a number represented as an array
using System;
using System.Collections.Generic;
class Node {
public int data;
public int both; // XOR of previous and next node ids
public int id;
public static int nextId = 1;
public Node(int data) {
this.data = data;
this.both = 0;
this.id = nextId++;
GFG.memory[this.id] = this;
}
}
class GFG {
Node head;
Node tail;
public static Dictionary<int, Node> memory = new Dictionary<int, Node>();
// XOR function for Node pointers
Node XOR(Node a, Node b) {
int idA = (a == null) ? 0 : a.id;
int idB = (b == null) ? 0 : b.id;
int xorId = idA ^ idB;
return memory.ContainsKey(xorId) ? memory[xorId] : null;
}
// Constructor to initialize an empty list
public GFG() {
head = tail = null;
}
// Insert a node at the head of the list
public void insertAtHead(int data) {
Node newNode = new Node(data);
newNode.both = (head == null) ? 0 : head.id;
if (head != null)
head.both = head.both ^ newNode.id;
else {
// If the list was empty, the new node is both the head and the tail
tail = newNode;
}
head = newNode;
}
// Insert a node at the tail of the list
public void insertAtTail(int data) {
Node newNode = new Node(data);
newNode.both = (tail == null) ? 0 : tail.id;
if (tail != null)
tail.both = tail.both ^ newNode.id;
else {
// If the list was empty, the new node is both the head and the tail
head = newNode;
}
tail = newNode;
}
// Delete a node from the head of the list
public void deleteFromHead() {
if (head == null)
return;
Node next = (head.both == 0) ? null : memory[head.both];
if (next != null) {
next.both = next.both ^ head.id;
} else {
tail = null;
}
memory.Remove(head.id);
head = next;
}
// Delete a node from the tail of the list
public void deleteFromTail() {
if (tail == null)
return;
Node prev = (tail.both == 0) ? null : memory[tail.both];
if (prev != null) {
prev.both = prev.both ^ tail.id;
} else {
head = null;
}
memory.Remove(tail.id);
tail = prev;
}
// Print the elements of the list
public void printList() {
Node current = head;
Node prev = null;
while (current != null) {
Console.Write(current.data + " ");
int nextId = current.both ^ (prev == null ? 0 : prev.id);
Node next = memory.ContainsKey(nextId) ? memory[nextId] : null;
prev = current;
current = next;
}
Console.WriteLine();
}
public static void Main() {
GFG list = new GFG();
list.insertAtHead(10);
list.insertAtHead(20);
list.insertAtTail(30);
list.insertAtTail(40);
// prints 20 10 30 40
list.printList();
list.deleteFromHead();
// prints 10 30 40
list.printList();
list.deleteFromTail();
// prints 10 30
list.printList();
}
}
// JavaScript program to implement XOR Linked List
// for reversing alternate levels of a binary tree is different,
// so here we implement XOR Linked List for adding nodes,
// deleting nodes, and printing the list
// Global memory object to simulate pointer addresses.
let memory = {};
let nextId = 1;
class Node {
constructor(data) {
this.data = data;
this.both = 0; // XOR of previous and next node ids
this.id = nextId++;
memory[this.id] = this;
}
}
class GFG {
constructor() {
this.head = null;
this.tail = null;
}
// XOR function for Node pointers
XOR(a, b) {
let idA = (a === null) ? 0 : a.id;
let idB = (b === null) ? 0 : b.id;
let xorId = idA ^ idB;
return memory[xorId] || null;
}
// Insert a node at the head of the list
insertAtHead(data) {
let newNode = new Node(data);
newNode.both = (this.head === null) ? 0 : this.head.id;
if (this.head !== null)
this.head.both = this.head.both ^ newNode.id;
else {
// If the list was empty, the new node is both the head and the tail
this.tail = newNode;
}
this.head = newNode;
}
// Insert a node at the tail of the list
insertAtTail(data) {
let newNode = new Node(data);
newNode.both = (this.tail === null) ? 0 : this.tail.id;
if (this.tail !== null)
this.tail.both = this.tail.both ^ newNode.id;
else {
// If the list was empty, the new node is both the head and the tail
this.head = newNode;
}
this.tail = newNode;
}
// Delete a node from the head of the list
deleteFromHead() {
if (this.head === null)
return;
let temp = this.head;
let next = (this.head.both === 0) ? null : memory[this.head.both];
if (next !== null) {
next.both = next.both ^ this.head.id;
} else {
this.tail = null;
}
delete memory[this.head.id];
this.head = next;
}
// Delete a node from the tail of the list
deleteFromTail() {
if (this.tail === null)
return;
let temp = this.tail;
let prev = (this.tail.both === 0) ? null : memory[this.tail.both];
if (prev !== null) {
prev.both = prev.both ^ this.tail.id;
} else {
this.head = null;
}
delete memory[this.tail.id];
this.tail = prev;
}
// Print the elements of the list
printList() {
let current = this.head;
let prev = null;
let output = "";
while (current !== null) {
output += current.data + " ";
let nextId = current.both ^ (prev === null ? 0 : prev.id);
prev = current;
current = memory[nextId] || null;
}
console.log(output);
}
}
// Driver Code
(function() {
let list = new GFG();
list.insertAtHead(10);
list.insertAtHead(20);
list.insertAtTail(30);
list.insertAtTail(40);
// prints 20 10 30 40
list.printList();
list.deleteFromHead();
// prints 10 30 40
list.printList();
list.deleteFromTail();
// prints 10 30
list.printList();
})();
Output
20 10 30 40 10 30 40 10 30
Time Complexity: O(n)
Auxiliary Space: O(1)
Advantages and Disadvantages Of XOR Linked List:
Advantages:
- XOR linked lists use less memory compared to traditional doubly linked lists. This is because they only need one “pointer” (the XOR of the previous and next pointers) instead of two separate pointers, which can save memory in applications where memory is a critical resource.
- XOR linked lists can be traversed in both directions (forward and backward) without the need for an additional pointer to the previous node.
- Insertion and deletion at both the head and tail of the list can be done in constant time (O(1)), just like in traditional singly linked lists. This makes them efficient for certain operations.
Disadvantages:
- XOR linked lists are more complex to implement and maintain than traditional linked lists.
- XOR linked lists are not a standard data structure in most programming languages and libraries.