Why Doubly Linked List Over Singly Linked List in DSA C - Performance Analysis
We want to understand how using a doubly linked list changes the time it takes to do common operations compared to a singly linked list.
Which operations become faster or slower, and why?
Analyze the time complexity of inserting and deleting nodes in a doubly linked list.
// Insert a new node after a given node in doubly linked list
void insertAfter(Node* prev_node, int new_data) {
if (prev_node == NULL) return;
Node* new_node = (Node*)malloc(sizeof(Node));
new_node->data = new_data;
new_node->next = prev_node->next;
new_node->prev = prev_node;
if (prev_node->next != NULL) prev_node->next->prev = new_node;
prev_node->next = new_node;
}
// Delete a given node from doubly linked list
void deleteNode(Node** head_ref, Node* del_node) {
if (*head_ref == NULL || del_node == NULL) return;
if (*head_ref == del_node) *head_ref = del_node->next;
if (del_node->next != NULL) del_node->next->prev = del_node->prev;
if (del_node->prev != NULL) del_node->prev->next = del_node->next;
free(del_node);
}
This code shows how to insert and delete nodes when you have access to the node itself in a doubly linked list.
Look at what repeats or dominates the work done.
- Primary operation: Adjusting pointers (prev and next) for nodes.
- How many times: Each insertion or deletion changes a fixed number of pointers, so constant time per operation.
Since insertion and deletion here happen by changing a few pointers, the time does not grow with the list size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 5-6 pointer changes |
| 100 | 5-6 pointer changes |
| 1000 | 5-6 pointer changes |
Pattern observation: The number of pointer changes stays the same no matter how big the list is.
Time Complexity: O(1)
This means insertion or deletion at a known node happens in constant time, regardless of list size.
[X] Wrong: "Doubly linked lists always take longer because they have more pointers to update."
[OK] Correct: Actually, having two pointers lets you delete or insert nodes faster when you have direct access, because you don't need to find the previous node by traversing.
Understanding when doubly linked lists improve operation speed shows you can choose the right data structure for the task, a key skill in coding interviews.
"What if we only had a singly linked list and wanted to delete a node given only that node? How would the time complexity change?"
Practice
Solution
Step 1: Understand traversal capabilities
A singly linked list allows traversal only in one direction, from head to tail.Step 2: Compare with doubly linked list
A doubly linked list stores two pointers per node, allowing traversal forward and backward.Final Answer:
Because it allows traversal in both forward and backward directions -> Option DQuick Check:
Traversal direction = Both [OK]
- Thinking doubly linked lists use less memory
- Confusing traversal directions
- Assuming singly linked lists allow backward traversal
Solution
Step 1: Identify required pointers for doubly linked list node
A doubly linked list node must have pointers to both previous and next nodes.Step 2: Check each option
struct Node { int data; struct Node* prev; struct Node* next; }; correctly declares bothprevandnextpointers as struct Node pointers.Final Answer:
struct Node { int data; struct Node* prev; struct Node* next; }; -> Option CQuick Check:
Node pointers = prev and next [OK]
- Omitting prev pointer
- Using int instead of pointer for next/prev
- Confusing singly linked list node with doubly linked
typedef struct Node {
int data;
struct Node* prev;
struct Node* next;
} Node;
Node* head = NULL;
// Function to add node at end
void append(int val) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = val;
newNode->next = NULL;
if (head == NULL) {
newNode->prev = NULL;
head = newNode;
return;
}
Node* temp = head;
while (temp->next != NULL) temp = temp->next;
temp->next = newNode;
newNode->prev = temp;
}
// Print list forward
void printForward() {
Node* temp = head;
while (temp != NULL) {
printf("%d ", temp->data);
temp = temp->next;
}
}
int main() {
append(10);
append(20);
append(30);
printForward();
return 0;
}Solution
Step 1: Trace append function calls
append(10) creates first node with data 10, head points to it. append(20) adds node with 20 at end. append(30) adds node with 30 at end.Step 2: Trace printForward function
printForward starts at head and prints data while moving next. It prints 10, then 20, then 30.Final Answer:
10 20 30 -> Option AQuick Check:
List forward print = 10 20 30 [OK]
- Confusing forward and backward traversal
- Assuming printForward prints in reverse
- Missing NULL checks causing errors
void deleteLast() {
if (head == NULL) return;
Node* temp = head;
while (temp->next != NULL) {
temp = temp->next;
}
temp->prev->next = NULL;
free(temp);
}Solution
Step 1: Analyze deletion logic
The code finds last node by moving to node where next is NULL, then sets previous node's next to NULL and frees last node.Step 2: Check edge case for single node list
If list has only one node, temp->prev is NULL, so temp->prev->next causes a crash or undefined behavior.Final Answer:
Does not handle case when list has only one node -> Option BQuick Check:
Single node deletion needs special handling [OK]
- Ignoring single node edge case
- Assuming prev pointer always exists
- Not freeing memory after deletion
Solution
Step 1: Understand feature requirements
The feature requires moving backward and forward through the list of files.Step 2: Match data structure capabilities
Singly linked lists allow only forward traversal, arrays allow random access but not efficient insertion/deletion, doubly linked lists allow easy backward and forward traversal.Step 3: Evaluate options
Doubly linked list fits best because it supports moving backward and forward efficiently.Final Answer:
Doubly linked list, because it allows moving backward and forward easily -> Option AQuick Check:
Backward traversal = Doubly linked list [OK]
- Choosing singly linked list for backward moves
- Ignoring traversal needs
- Assuming arrays handle backward traversal efficiently
Ready to prove it?
Pass the test and this pattern moves unknown
