Doubly Linked Lists: One Step at a Time!

Before starting this tutorial, make sure to check out Linked Lists: One Step at a Time! to understand the basics of singly linked lists first.
First Step: What is a doubly linked list node made up of?
Explanation:
A doubly linked list is made up of nodes, just like a singly linked list.
Each node contains some data and TWO pointers: one to the next node and one to the previous node.
This allows traversal in both directions (forward and backward).
Define a structure for the node that includes an integer and pointers to both next and previous nodes.
Code:
#include <stdio.h>
#include <stdlib.h>
// Define the doubly linked list node structure
struct Node {
int data; // Integer data
struct Node* next; // Pointer to the next node
struct Node* prev; // Pointer to the previous node
};
Second Step: Creating Two Nodes and Linking Them
Explanation:
Create two nodes.
Set the first node's next pointer to the second node.
Set the second node's previous pointer to the first node.
Set the first node's previous pointer to NULL (start of list).
Set the second node's next pointer to NULL (end of list).
Assign values to the nodes using
scanf
.
Code:
#include <stdio.h>
#include <stdlib.h>
// Define the node structure
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
int main() {
struct Node* first = (struct Node*)malloc(sizeof(struct Node));
struct Node* second = (struct Node*)malloc(sizeof(struct Node));
// Get values from the user
printf("Enter value for the first node: ");
scanf("%d", &first->data);
first->prev = NULL;
first->next = second;
printf("Enter value for the second node: ");
scanf("%d", &second->data);
second->prev = first;
second->next = NULL;
// Free allocated memory
free(first);
free(second);
return 0;
}
Third Step: Creating Three Nodes and Linking Them
Explanation:
Create three nodes.
Link them in a chain where each node points to the next and previous nodes.
The first node's previous pointer is NULL.
The last node's next pointer is NULL.
Code:
#include <stdio.h>
#include <stdlib.h>
// Define the node structure
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
int main() {
struct Node* first = (struct Node*)malloc(sizeof(struct Node));
struct Node* second = (struct Node*)malloc(sizeof(struct Node));
struct Node* third = (struct Node*)malloc(sizeof(struct Node));
// Get values from the user
printf("Enter value for the first node: ");
scanf("%d", &first->data);
first->prev = NULL;
first->next = second;
printf("Enter value for the second node: ");
scanf("%d", &second->data);
second->prev = first;
second->next = third;
printf("Enter value for the third node: ");
scanf("%d", &third->data);
third->prev = second;
third->next = NULL;
// Free allocated memory
free(first);
free(second);
free(third);
return 0;
}
Fourth Step: Creating a Doubly Linked List of 10 Nodes in a Loop
Explanation:
Create a doubly linked list with 10 nodes using a loop.
Use a head pointer to point to the first node.
Use temporary pointers to build the list.
Each new node must link to the previous node (if any).
Use
scanf
to get values for each node from the user.
Code:
#include <stdio.h>
#include <stdlib.h>
// Define the node structure
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
int main() {
struct Node* head = NULL; // Head of the list
struct Node* temp = NULL; // Temporary pointer
struct Node* newNode = NULL; // Pointer to new node
// Create 10 nodes
for (int i = 0; i < 10; i++) {
newNode = (struct Node*)malloc(sizeof(struct Node));
printf("Enter value for node %d: ", i + 1);
scanf("%d", &newNode->data);
newNode->next = NULL;
newNode->prev = NULL;
if (head == NULL) {
// If the list is empty, set head to the new node
head = newNode;
} else {
// Otherwise, link the new node to the last node
temp->next = newNode;
newNode->prev = temp;
}
// Move the temp pointer to the new node
temp = newNode;
}
return 0;
}
Fifth Step: Printing the Doubly Linked List (Forward and Backward)
Explanation:
Write functions to print the list in both directions.
For forward traversal, start from head and move using next pointers.
For backward traversal, first find the tail, then move using prev pointers.
Print the data of each node.
Code:
#include <stdio.h>
#include <stdlib.h>
// Define the node structure
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
// Function to print the linked list forward
void printListForward(struct Node* head) {
struct Node* temp = head;
printf("Forward: ");
while (temp != NULL) {
printf("%d <-> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
// Function to print the linked list backward
void printListBackward(struct Node* head) {
struct Node* temp = head;
// Find the tail node
if (temp == NULL) return;
while (temp->next != NULL) {
temp = temp->next;
}
// Print from tail to head
printf("Backward: ");
while (temp != NULL) {
printf("%d <-> ", temp->data);
temp = temp->prev;
}
printf("NULL\n");
}
int main() {
struct Node* head = NULL;
struct Node* temp = NULL;
struct Node* newNode = NULL;
// Create 5 nodes for demonstration
for (int i = 0; i < 5; i++) {
newNode = (struct Node*)malloc(sizeof(struct Node));
printf("Enter value for node %d: ", i + 1);
scanf("%d", &newNode->data);
newNode->next = NULL;
newNode->prev = NULL;
if (head == NULL) {
head = newNode;
} else {
temp->next = newNode;
newNode->prev = temp;
}
temp = newNode;
}
// Print the list in both directions
printListForward(head);
printListBackward(head);
// Free allocated memory
temp = head;
while (temp != NULL) {
struct Node* next = temp->next;
free(temp);
temp = next;
}
return 0;
}
Sixth Step: Adding a Node to the Beginning
Explanation:
Write a function that takes a double pointer to the head of the list.
Create a new node, assign its value from the function parameter.
Make the new node's next pointer point to the current head.
If head exists, update its previous pointer to point to the new node.
Update the head to point to the new node.
Use
printListForward
to print the updated list.
Code:
#include <stdio.h>
#include <stdlib.h>
// Define the node structure
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
// Function to print the linked list forward
void printListForward(struct Node* head) {
struct Node* temp = head;
printf("Forward: ");
while (temp != NULL) {
printf("%d <-> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
// Function to add a node at the beginning of the list
void addNodeAtBeginning(struct Node** pointerToHead, int value) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = *pointerToHead;
newNode->prev = NULL;
if (*pointerToHead != NULL) {
(*pointerToHead)->prev = newNode;
}
*pointerToHead = newNode;
}
int main() {
struct Node* head = NULL;
struct Node* temp = NULL;
struct Node* newNode = NULL;
// Create 3 nodes
for (int i = 0; i < 3; i++) {
newNode = (struct Node*)malloc(sizeof(struct Node));
printf("Enter value for node %d: ", i + 1);
scanf("%d", &newNode->data);
newNode->next = NULL;
newNode->prev = NULL;
if (head == NULL) {
head = newNode;
} else {
temp->next = newNode;
newNode->prev = temp;
}
temp = newNode;
}
// Add a new node at the beginning
int newValue;
printf("Enter value for the new node at the beginning: ");
scanf("%d", &newValue);
addNodeAtBeginning(&head, newValue);
// Print the list
printListForward(head);
// Free allocated memory
temp = head;
while (temp != NULL) {
struct Node* next = temp->next;
free(temp);
temp = next;
}
return 0;
}
Seventh Step: Inserting a Node After the Nth Node
Explanation:
Write a function that takes the head of the list, a position
n
, and a value.Traverse the list to find the nth node.
If the nth node exists, create a new node.
Update the links: new node's prev points to nth node, new node's next points to nth node's next.
If nth node's next exists, update its prev to point to new node.
Update nth node's next to point to new node.
Code:
#include <stdio.h>
#include <stdlib.h>
// Define the node structure
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
// Function to print the linked list forward
void printListForward(struct Node* head) {
struct Node* temp = head;
printf("Forward: ");
while (temp != NULL) {
printf("%d <-> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
// Function to insert a node after the nth node
void insertNodeAfterNth(struct Node* head, int n, int value) {
struct Node* temp = head;
// Traverse to the nth node
for (int i = 0; i < n; i++) {
if (temp == NULL) return; // If n is beyond the end of the list
temp = temp->next;
}
if (temp != NULL) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = temp->next;
newNode->prev = temp;
if (temp->next != NULL) {
temp->next->prev = newNode;
}
temp->next = newNode;
}
}
int main() {
struct Node* head = NULL;
// Create a simple list with 3 nodes for testing
head = (struct Node*)malloc(sizeof(struct Node));
head->data = 1;
head->prev = NULL;
head->next = (struct Node*)malloc(sizeof(struct Node));
head->next->data = 2;
head->next->prev = head;
head->next->next = (struct Node*)malloc(sizeof(struct Node));
head->next->next->data = 3;
head->next->next->prev = head->next;
head->next->next->next = NULL;
printf("Original list:\n");
printListForward(head);
// Insert a new node after the nth node
int n = 1, value = 99;
insertNodeAfterNth(head, n, value);
printf("After inserting %d after position %d:\n", value, n);
printListForward(head);
// Free allocated memory
struct Node* temp = head;
while (temp != NULL) {
struct Node* next = temp->next;
free(temp);
temp = next;
}
return 0;
}
Eighth Step: Deleting the Head Node
Explanation:
Write a function that takes a double pointer to the head of the list.
If the list is not empty, save the current head.
Make the head point to the second node.
If the new head exists, update its prev pointer to NULL.
Free the memory of the old head node.
Code:
#include <stdio.h>
#include <stdlib.h>
// Define the node structure
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
// Function to print the linked list forward
void printListForward(struct Node* head) {
struct Node* temp = head;
printf("Forward: ");
while (temp != NULL) {
printf("%d <-> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
// Function to delete the head node
void deleteHeadNode(struct Node** head) {
if (*head != NULL) {
struct Node* temp = *head;
*head = (*head)->next;
if (*head != NULL) {
(*head)->prev = NULL;
}
free(temp);
}
}
int main() {
struct Node* head = NULL;
// Create a simple list with 3 nodes for testing
head = (struct Node*)malloc(sizeof(struct Node));
head->data = 1;
head->prev = NULL;
head->next = (struct Node*)malloc(sizeof(struct Node));
head->next->data = 2;
head->next->prev = head;
head->next->next = (struct Node*)malloc(sizeof(struct Node));
head->next->next->data = 3;
head->next->next->prev = head->next;
head->next->next->next = NULL;
// Print the list
printf("Original list:\n");
printListForward(head);
// Delete the head node
deleteHeadNode(&head);
// Print the list again
printf("After deleting head:\n");
printListForward(head);
// Free allocated memory
struct Node* temp = head;
while (temp != NULL) {
struct Node* next = temp->next;
free(temp);
temp = next;
}
return 0;
}
Ninth Step: Deleting a Node with a Specific Value
Explanation:
Write a function that takes a double pointer to the head of the list and a value.
Traverse the list to find the node with the given value.
If found, update the links to bypass the node.
Handle three cases: deleting head node, deleting middle node, deleting tail node.
Free the memory of the deleted node.
Code:
#include <stdio.h>
#include <stdlib.h>
// Define the node structure
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
// Function to print the linked list forward
void printListForward(struct Node* head) {
struct Node* temp = head;
printf("Forward: ");
while (temp != NULL) {
printf("%d <-> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
// Function to delete a node with a specific value
void deleteNodeWithValue(struct Node** head, int value) {
struct Node* temp = *head;
// Search for the node with the given value
while (temp != NULL && temp->data != value) {
temp = temp->next;
}
// If the value was not found
if (temp == NULL) return;
// If it's the head node
if (temp == *head) {
*head = temp->next;
if (*head != NULL) {
(*head)->prev = NULL;
}
}
// If it's not the head node
else {
// Update the previous node's next pointer
if (temp->prev != NULL) {
temp->prev->next = temp->next;
}
// Update the next node's prev pointer
if (temp->next != NULL) {
temp->next->prev = temp->prev;
}
}
free(temp);
}
int main() {
struct Node* head = NULL;
// Create a simple list with 3 nodes for testing
head = (struct Node*)malloc(sizeof(struct Node));
head->data = 1;
head->prev = NULL;
head->next = (struct Node*)malloc(sizeof(struct Node));
head->next->data = 2;
head->next->prev = head;
head->next->next = (struct Node*)malloc(sizeof(struct Node));
head->next->next->data = 3;
head->next->next->prev = head->next;
head->next->next->next = NULL;
// Print the list
printf("Original list:\n");
printListForward(head);
// Delete a node with a specific value
int valueToDelete = 2;
deleteNodeWithValue(&head, valueToDelete);
// Print the list again
printf("After deleting node with value %d:\n", valueToDelete);
printListForward(head);
// Free allocated memory
struct Node* temp = head;
while (temp != NULL) {
struct Node* next = temp->next;
free(temp);
temp = next;
}
return 0;
}
Bonus Step: Reversing a Doubly Linked List
Explanation:
Write a function that reverses the entire doubly linked list.
Traverse the list and swap the next and prev pointers for each node.
Update the head to point to the last node (which becomes the new first node).
Code:
#include <stdio.h>
#include <stdlib.h>
// Define the node structure
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
// Function to print the linked list forward
void printListForward(struct Node* head) {
struct Node* temp = head;
printf("Forward: ");
while (temp != NULL) {
printf("%d <-> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
// Function to reverse the doubly linked list
void reverseList(struct Node** head) {
struct Node* temp = NULL;
struct Node* current = *head;
// Swap next and prev for all nodes
while (current != NULL) {
temp = current->prev;
current->prev = current->next;
current->next = temp;
current = current->prev;
}
// Before changing head, check for empty list or single node
if (temp != NULL) {
*head = temp->prev;
}
}
int main() {
struct Node* head = NULL;
// Create a simple list with 4 nodes for testing
head = (struct Node*)malloc(sizeof(struct Node));
head->data = 1;
head->prev = NULL;
head->next = (struct Node*)malloc(sizeof(struct Node));
head->next->data = 2;
head->next->prev = head;
head->next->next = (struct Node*)malloc(sizeof(struct Node));
head->next->next->data = 3;
head->next->next->prev = head->next;
head->next->next->next = (struct Node*)malloc(sizeof(struct Node));
head->next->next->next->data = 4;
head->next->next->next->prev = head->next->next;
head->next->next->next->next = NULL;
// Print the original list
printf("Original list:\n");
printListForward(head);
// Reverse the list
reverseList(&head);
// Print the reversed list
printf("Reversed list:\n");
printListForward(head);
// Free allocated memory
struct Node* temp = head;
while (temp != NULL) {
struct Node* next = temp->next;
free(temp);
temp = next;
}
return 0;
}
Subscribe to my newsletter
Read articles from Jyotiprakash Mishra directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Jyotiprakash Mishra
Jyotiprakash Mishra
I am Jyotiprakash, a deeply driven computer systems engineer, software developer, teacher, and philosopher. With a decade of professional experience, I have contributed to various cutting-edge software products in network security, mobile apps, and healthcare software at renowned companies like Oracle, Yahoo, and Epic. My academic journey has taken me to prestigious institutions such as the University of Wisconsin-Madison and BITS Pilani in India, where I consistently ranked among the top of my class. At my core, I am a computer enthusiast with a profound interest in understanding the intricacies of computer programming. My skills are not limited to application programming in Java; I have also delved deeply into computer hardware, learning about various architectures, low-level assembly programming, Linux kernel implementation, and writing device drivers. The contributions of Linus Torvalds, Ken Thompson, and Dennis Ritchie—who revolutionized the computer industry—inspire me. I believe that real contributions to computer science are made by mastering all levels of abstraction and understanding systems inside out. In addition to my professional pursuits, I am passionate about teaching and sharing knowledge. I have spent two years as a teaching assistant at UW Madison, where I taught complex concepts in operating systems, computer graphics, and data structures to both graduate and undergraduate students. Currently, I am an assistant professor at KIIT, Bhubaneswar, where I continue to teach computer science to undergraduate and graduate students. I am also working on writing a few free books on systems programming, as I believe in freely sharing knowledge to empower others.