Doubly Linked List
A Doubly Linked List (DLL) contains an extra pointer namely “previous pointer” together with the “next pointer”. Other than this, it is similar to Singly Linked List.
Representation of Doubly Linked list
//Node of a Doubly Linked List struct Node { int data; struct Node *next; //Pointer to next node in DLL struct Node *prev; //Pointer to previous node in DLL }
Advantages
over Singly Linked List
Disadvantages
over Singly Linked List
Insertion
in Doubly linked list
A node can be inserted in four ways:
Add a node
at the front
A new node is always added to the front of the list. The newly added node becomes the new head of the DLL.
Below is the method push() for adding a node at the front
void push(struct Node** head_ref, int new_data) { struct Node* new_node = (struct Node*)malloc(sizeof(struct Node)); //Assign a data to the node new_node->data = new_data; //Adjusting the links new_node->next = (*head_ref); new_node->prev = NULL; if ((*head_ref) != NULL) (*head_ref)->prev = new_node; //Assigning head to the newly added node (*head_ref) = new_node; }
Add a node
after the given node
The pointer to the node as prev_node will be given and the new node is inserted after the given node. The node P is inserted after the given node T. The list is traversed till node T and the new node is inserted.
From the above figure, the
node D is inserted after the node B. The previous and next pointer is
reassigned. Now, the previous pointer of node D points to node B, the next
pointer of node B points to node D, the previous pointer of node C points to
node D and the next pointer of node D points to node C.
Given the below method insertAfter() will add a node after the given node:
void insertAfter(struct Node* prev_node, int new_data) { //check if the given prev_node is NULL if (prev_node == NULL) { printf("the given previous node cannot be NULL"); return; } struct Node* new_node = (struct Node*)malloc(sizeof(struct Node)); //Assign the data new_node->data = new_data; //Make next of new node as next of prev_node new_node->next = prev_node->next; //Make the next of prev_node as new_node prev_node->next = new_node; //Make prev_node as previous of new_node new_node->prev = prev_node; //Change previous of new_node's next node if (new_node->next != NULL) new_node->next->prev = new_node; }
Add a node
at the end
The new node is added at the end of the DLL. Since the Linked list is represented by the head of it, we have to traverse till the end and have to change the next of the last node to the new node and previous of new node as the last node of the list.
Given the below method append() will add a node at the end:
void append(struct Node** head_ref, int new_data) { struct Node* new_node = (struct Node*)malloc(sizeof(struct Node)); struct Node* last = *head_ref; //Assign the data new_node->data = new_data; //This next of new_node is NULL as it is the last node in the list new_node->next = NULL; //If the list is empty, this is the only node in the list if (*head_ref == NULL) { new_node->prev = NULL; *head_ref = new_node; return; } //Traverse till the end while (last->next != NULL) last = last->next; //Adjusting the links last->next = new_node; new_node->prev = last; return; }
From the above code, we add the new node at the end hence the next
of newly added node is null and the next of the last node is newly added node.
The time complexity for appending the new node at the end is O(n).
Add a node
before a given node
The new node will be added before to the given node pointer. With
the previous pointer of the given node pointer, will be assigned to the new
node and the next of the new node will be the given node pointer. The previous
node of the new node will be the previous of the given node pointer.
In the below figure, the given node pointer P points to the node C, the new node Z will be added before a given node P.
Given below method addBefore(), inserts the new node before the given node pointer.
void insertBefore(struct Node** head_ref, struct Node* next_node, int new_data) { if (next_node == NULL) { printf("the given next node cannot be NULL"); return; } struct Node* new_node = (struct Node*)malloc(sizeof(struct Node)); //Assign the data new_node->data = new_data; //Make prev of new node as prev of next_node new_node->prev = next_node->prev; //Make the prev of next_node as new_node next_node->prev = new_node; //Make next_node as next of new_node new_node->next = next_node; //Change next of new_node's previous node if (new_node->prev != NULL) new_node->prev->next = new_node; //If the prev of new_node is NULL, it will be the new head node else (*head_ref) = new_node; }
From the above code, the new node is added before the given node
next_node.
Doubly
Linked List – Deletion
The deletion in doubly linked list is very much similar to singly linked list. In doubly linked list, we adjust the prev pointer in addition to the next pointer.
Let the node to be deleted in doubly linked list be del
Below is the code to delete the node in the Doubly linked list when the pointer of the node to be deleted link is given,
void deleteNode(struct Node** head_ref, struct Node* del) { //Check whether the given list and the node to be deleted are NULL if (*head_ref == NULL || del == NULL) return; //If node to be deleted is head node if (*head_ref == del) *head_ref = del->next; //Change next only if node to be deleted is NOT the last node if (del->next != NULL) del->next->prev = del->prev; //Change prev only if node to be deleted is NOT the first node if (del->prev != NULL) del->prev->next = del->next; //Free the memory allocated to the deleted node free(del); return; }
The time complexity for the above code is O(1).