Doubly Linked List Structure and Node Design in DSA C - Time & Space Complexity
We want to understand how the time to work with a doubly linked list changes as it grows.
Specifically, how fast can we access or change nodes based on the list size?
Analyze the time complexity of the following code snippet.
// Define a node in a doubly linked list
struct Node {
int data;
struct Node* prev;
struct Node* next;
};
// Create a new node
struct Node* createNode(int value) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}
This code creates a single node with pointers to the previous and next nodes.
In this snippet, there are no loops or recursion.
- Primary operation: Allocating and initializing one node.
- How many times: Exactly once per call.
Since this creates only one node at a time, the work does not grow with the list size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 1 node creation |
| 100 | 1 node creation |
| 1000 | 1 node creation |
Pattern observation: The time to create a node stays the same no matter how big the list is.
Time Complexity: O(1)
This means creating a node takes the same amount of time regardless of list size.
[X] Wrong: "Creating a node takes longer as the list grows because it has to connect to all nodes."
[OK] Correct: Creating a node only sets its own pointers; linking it to the list happens separately and usually involves just a few pointer changes.
Understanding node creation time helps you explain how linked lists build up and why some operations are fast or slow.
"What if we added a loop to traverse the list to find the last node before adding the new node? How would the time complexity change?"
Practice
Solution
Step 1: Understand node pointers in singly linked list
A singly linked list node has only one pointer to the next node.Step 2: Understand node pointers in doubly linked list
A doubly linked list node has two pointers: one to the next node and one to the previous node.Final Answer:
A doubly linked list node has pointers to both the previous and next nodes. -> Option AQuick Check:
Doubly linked list node = two pointers [OK]
- Thinking doubly linked nodes store data twice
- Confusing singly and doubly linked list pointers
- Assuming doubly linked nodes have only one pointer
Solution
Step 1: Identify required pointers in doubly linked list node
It must have pointers to both previous and next nodes of type struct Node*.Step 2: Check each option for correct pointer types and members
typedef struct Node { int data; struct Node* prev; struct Node* next; } Node; correctly defines data, prev, and next pointers as struct Node*.Final Answer:
typedef struct Node { int data; struct Node* prev; struct Node* next; } Node; -> Option CQuick Check:
Correct struct has prev and next pointers [OK]
- Missing prev pointer in struct
- Using wrong pointer types like int* for nodes
- Defining only one pointer instead of two
Node* createNode(int val) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = val;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}
int main() {
Node* node = createNode(10);
printf("%d %p %p", node->data, node->prev, node->next);
return 0;
}What will be the output of this program?
Solution
Step 1: Analyze node initialization
Node data is set to 10, prev and next pointers are set to NULL (which is 0x0 in memory).Step 2: Understand printf output for pointers
Printing pointers with %p shows their memory address; NULL prints as 0x0.Final Answer:
10 0x0 0x0 -> Option BQuick Check:
NULL pointers print as 0x0 in %p format [OK]
- Expecting 'NULL' string instead of 0x0
- Assuming garbage values for initialized pointers
- Confusing pointer print format specifiers
void insertAtHead(Node** head, int val) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = val;
newNode->prev = NULL;
newNode->next = *head;
*head = newNode;
if (*head != NULL) {
(*head)->prev = newNode;
}
}Solution
Step 1: Trace pointer updates
newNode->next is set to current head, then head is updated to newNode.Step 2: Check the if condition usage
After updating *head, the condition if (*head != NULL) is always true because *head is newNode. The intention is to update old head's prev pointer.Step 3: Correct order
The if condition should check if old head (newNode->next) is not NULL before setting its prev pointer.Final Answer:
The condition if (*head != NULL) should be before updating *head. -> Option AQuick Check:
Check old head before changing pointers [OK]
- Checking *head after it points to newNode
- Setting prev pointer incorrectly
- Ignoring NULL checks before pointer updates
Solution
Step 1: Understand deletion in doubly linked list
To delete a node, update the previous node's next and next node's prev pointers to bypass the node.Step 2: Check necessity of setting data to zero
Setting the node's data to zero is unnecessary because the node will be freed and memory reclaimed.Step 3: Confirm freeing memory
Freeing the node's memory is essential to avoid memory leaks.Final Answer:
Set the node's data to zero before deletion. -> Option DQuick Check:
Data reset before free is unnecessary [OK]
- Clearing data before freeing memory
- Forgetting to update prev or next pointers
- Not freeing the node memory
Ready to prove it?
Pass the test and this pattern moves unknown
