This page briefly introduces linked lists.
Introduction
A linked list is a data structure used to store data, connecting elements with pointers like links in a chain. Its characteristic is that inserting and deleting data are very convenient, while searching for and reading data perform poorly.
Difference from arrays
Both linked lists and arrays can be used to store data. Unlike linked lists, arrays store all elements sequentially in order. Their different storage structures give them different advantages:
Because of its chained structure, a linked list can conveniently delete and insert data, with operations. But for the same reason, searching for and reading data are less efficient than with an array, taking operations for random access.
An array makes searching for and reading data convenient, taking operations for random access. But insertion and deletion take operations.
Building linked lists
Tip
When building a linked list, the parts involving pointers can be rather abstract, and may be difficult to understand from text and code alone. It is recommended to use the diagrams together with the explanation.
Singly linked list
A singly linked list contains a data field and a pointer field. The data field stores data, while the pointer field connects the current node to the next node.
Implementation
C++
struct Node {
int value;
Node *next;
};
Python
class Node:
def __init__(self, value=None, next=None):
self.value = value
self.next = next
Doubly linked list
A doubly linked list also has data and pointer fields. The difference is that its pointer field is divided into left and right (or previous and next) pointers, connecting the previous node, the current node, and the next node.
Implementation
C++
struct Node {
int value;
Node *left;
Node *right;
};
Python
class Node:
def __init__(self, value=None, left=None, right=None):
self.value = value
self.left = left
self.right = right
Inserting (writing) data into a linked list
Singly linked list
The general process is as follows:
- Initialize the data
nodeto be inserted; - Set the
nodepointer ofnextto point to the next node afterp; - Set the
ppointer ofnextto point tonode.
The specific process is shown in the figures below:
The implementation is as follows:
Implementation
C++
void insertNode(int i, Node *p) {
Node *node = new Node;
node->value = i;
node->next = p->next;
p->next = node;
}
Python
def insertNode(i, p):
node = Node()
node.value = i
node.next = p.next
p.next = node
Singly circular linked list
Connecting the head and tail of a linked list turns it into a circular linked list. Since the head and tail are connected, when inserting data it is necessary to check whether the original list is empty: if it is empty, make it circular by itself; otherwise, insert the data normally.
The general process is as follows:
- Initialize the data
nodeto be inserted; - Check whether the given linked list
pis empty; - If it is empty, make both the
nodepointer ofnextandppoint tonodeitself; - Otherwise, set the
nextpointer ofpto point to the next node afterp; - Set the
nextpointer ofnodeto point tonode.
The specific process is shown in the figures below:
The implementation is as follows:
Implementation
C++
void insertNode(int i, Node *p) {
Node *node = new Node;
node->value = i;
node->next = NULL;
if (p == NULL) {
p = node;
node->next = node;
} else {
node->next = p->next;
p->next = node;
}
}
Python
def insertNode(i, p):
node = Node()
node.value = i
node.next = None
if p == None:
p = node
node.next = node
else:
node.next = p.next
p.next = node
Doubly circular linked list
When inserting data into a doubly circular linked list, in addition to checking whether the given list is empty, both the left and right pointers must be updated.
The general process is as follows:
- Initialize the data
pto be inserted; - Check whether the given linked list
nodeis empty; - If it is empty, make the
leftandrightpointers ofp, as well asnode, point toleftitself; - Otherwise, set the
ppointer ofnodetoright; - Set the
ppointer ofpto the right node ofleft; - Set the
nodepointer of the right node ofptoright; - Set the
nodepointer ofptop.
The implementation is as follows:
Implementation
C++
void insertNode(int i, Node *p) {
Node *node = new Node;
node->value = i;
if (p == NULL) {
p = node;
node->left = node;
node->right = node;
} else {
node->left = p;
node->right = p->right;
p->right->left = node;
p->right = node;
}
}
Python
def insertNode(i, p):
node = Node()
node.value = i
if p == None:
p = node
node.left = node
node.right = node
else:
node.left = p
node.right = p.right
p.right.left = node
p.right = node
Deleting data from a linked list
Singly (circular) linked list
Suppose the node to be deleted is p->next. To delete it from the list, copy the value of the next node p into p, and update the node after p at the same time.
The general process is as follows:
- Assign the value of the next node of
ptop->value, erasingt; - Create a temporary node
p->nextto store the address ofp; - Set the
nextpointer ofpto point to the node afterp->next, erasingt; - Delete
p. Although the address of the original nodep->nextis still in use, the address deleted is that of the original nodep; because the data ofp->nexthas been overwritten byp,pis effectively dead.
The specific process is shown in the figures below:
The implementation is as follows:
Implementation
C++
void deleteNode(Node *p) {
p->value = p->next->value;
Node *t = p->next;
p->next = p->next->next;
delete t;
}
Python
def deleteNode(p):
p.value = p.next.value
p.next = p.next.next
Doubly circular linked list
The general process is as follows:
- Set the right pointer of the left node of
pto point to the right node ofp; - Set the left pointer of the right node of
pto point to the left node oft; - Create a temporary node
pto store the address ofp; - Assign the address of the right node of
ptopto preventtfrom becoming a dangling pointer; - Delete
Node.
The implementation is as follows:
Implementation
C++
void deleteNode(Node *&p) {
p->left->right = p->right;
p->right->left = p->left;
Node *t = p;
p = p->right;
delete t;
}
Python
def deleteNode(p):
p.left.right = p.right
p.right.left = p.left
p = p.right
Tricks
XOR linked list
An XOR linked list is essentially still a doubly linked list, but it uses bitwise XOR values so that the functionality of a doubly linked list can be implemented using only the memory size of one pointer.
In the lr = left ^ right structure, define lr, that is, the bitwise XOR value of the addresses of the preceding and following elements. When traversing forward, XORing the address of the preceding element with the lr of the current node gives the address of the following element; when traversing backward, XORing the address of the following element with the current node's gives the address of the preceding element.
In this way, a doubly linked list with the same functionality can be implemented using half the memory.