Doubly linked list in Data Structures Theory - Time & Space Complexity
When working with doubly linked lists, it is important to understand how the time to perform operations changes as the list grows.
We want to know how fast we can add, remove, or find items as the list gets bigger.
Analyze the time complexity of the following code snippet.
class Node:
def __init__(self, data):
self.data = data
self.prev = None
self.next = None
class DoublyLinkedList:
def __init__(self):
self.head = None
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
current = self.head
while current.next:
current = current.next
current.next = new_node
new_node.prev = current
This code adds a new item to the end of a doubly linked list by walking through the list to find the last node.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The while loop that moves through each node until the end.
- How many times: It runs once for each node in the list until the last one is found.
As the list gets longer, the time to find the end grows roughly in direct proportion to the number of items.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 steps to reach the end |
| 100 | About 100 steps to reach the end |
| 1000 | About 1000 steps to reach the end |
Pattern observation: The time grows linearly as the list size increases.
Time Complexity: O(n)
This means the time to add an item at the end grows directly with the number of items in the list.
[X] Wrong: "Adding to the end of a doubly linked list always takes constant time because we can go backwards."
[OK] Correct: Without keeping a reference to the last node, we must walk from the start to find the end, which takes time proportional to the list size.
Understanding how linked list operations scale helps you explain your choices clearly and shows you know how data structures behave in real situations.
"What if we kept a reference to the tail node? How would the time complexity of appending change?"
Practice
doubly linked list compared to a singly linked list?Solution
Step 1: Understand node links in doubly linked lists
A doubly linked list node contains two links: one to the previous node and one to the next node.Step 2: Compare with singly linked list
A singly linked list node only has a link to the next node, not the previous one.Final Answer:
Each node has links to both the previous and next nodes -> Option CQuick Check:
Doubly linked list = two links per node [OK]
- Thinking nodes only link forward
- Confusing with arrays or stacks
- Assuming fixed size storage
newNode after a node current in a doubly linked list?Solution
Step 1: Set newNode's next and prev pointers
newNode.next should point to current.next, and newNode.prev should point to current.Step 2: Update surrounding nodes to link to newNode
current.next.prev must point back to newNode, and current.next must point to newNode.Final Answer:
newNode.next = current.next; newNode.prev = current; current.next.prev = newNode; current.next = newNode; -> Option AQuick Check:
Update newNode links, then fix neighbors [OK]
- Mixing prev and next pointers
- Not updating current.next.prev
- Assigning pointers in wrong order
Node1: data=10, prev=None, next=Node2
Node2: data=20, prev=Node1, next=Node3
Node3: data=30, prev=Node2, next=None
current = Node2
print(current.prev.data, current.data, current.next.data)
What will be the output?
Solution
Step 1: Identify current node and its neighbors
current is Node2 with data 20; prev is Node1 with data 10; next is Node3 with data 30.Step 2: Print data values of prev, current, and next nodes
Output will be 10 (prev), 20 (current), 30 (next).Final Answer:
10 20 30 -> Option DQuick Check:
Prev, current, next data = 10, 20, 30 [OK]
- Confusing prev and next nodes
- Assuming None has data
- Printing node references instead of data
current from a doubly linked list. What is the error?current.prev.next = current.next
current.next.prev = current.prev
current = None
Solution
Step 1: Analyze pointer updates for deletion
The code updates the previous node's next pointer and the next node's prev pointer to bypass current.Step 2: Check edge cases for head or tail nodes
If current is the head (no prev), current.prev is None, so current.prev.next causes an error.Final Answer:
It does not handle the case when current is the head node -> Option BQuick Check:
Deleting head needs special handling [OK]
- Ignoring head or tail node cases
- Assuming current is always in middle
- Confusing which pointers to update
next and prev pointers of each node. Which of the following steps is correct to achieve this?Solution
Step 1: Swap next and prev pointers for each node
Swapping next and prev reverses the direction of links.Step 2: Move to the new prev node after swapping
After swapping, the original next is now prev, so move to node.prev to continue.Final Answer:
For each node, swap its next and prev pointers, then move to the new prev node -> Option AQuick Check:
Swap links, then follow new prev to reverse [OK]
- Moving to node.next after swapping
- Swapping only one pointer
- Setting pointers to None incorrectly
Ready to prove it?
Pass the test and this pattern moves unknown
