Dequeue Using Linked List in DSA Python - Time & Space Complexity
We want to understand how fast operations like adding or removing items work in a dequeue built with a linked list.
How does the time taken change as we add more items?
Analyze the time complexity of these dequeue operations using a linked list.
class Node:
def __init__(self, data):
self.data = data
self.next = None
class Dequeue:
def __init__(self):
self.front = None
self.rear = None
def enqueue_front(self, data):
new_node = Node(data)
if not self.front:
self.front = self.rear = new_node
else:
new_node.next = self.front
self.front = new_node
def dequeue_rear(self):
if not self.rear:
return None
if self.front == self.rear:
temp = self.rear
self.front = self.rear = None
return temp.data
current = self.front
while current.next != self.rear:
current = current.next
temp = self.rear
self.rear = current
self.rear.next = None
return temp.data
This code adds an item at the front and removes an item from the rear of the dequeue using a linked list.
Look for loops or repeated steps in the code.
- Primary operation: The
whileloop indequeue_rearthat moves through nodes to find the one before the rear. - How many times: It runs once for each node except the last, so up to
n-1times if there arennodes.
As the number of items grows, the time to remove from the rear grows too because we must find the node before the last.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 9 steps to find the node before rear |
| 100 | About 99 steps |
| 1000 | About 999 steps |
Pattern observation: The steps increase roughly in a straight line with the number of items.
Time Complexity: O(n)
This means removing from the rear takes longer as the list grows, roughly proportional to the number of items.
[X] Wrong: "Removing from the rear is always fast because we have a pointer to the rear node."
[OK] Correct: Even with a rear pointer, we must find the node before rear to update its next link, which takes time proportional to the list size.
Understanding how linked list operations scale helps you explain trade-offs clearly and choose the right data structure for the job.
What if we kept a pointer to the node before rear? How would that change the time complexity of removing from the rear?
Practice
Solution
Step 1: Understand dequeue operations
A dequeue allows insertion and deletion from both front and rear ends.Step 2: Linked list implementation
Using a linked list, nodes can be added or removed from both ends efficiently.Final Answer:
A data structure where elements can be added or removed from both ends -> Option DQuick Check:
Dequeue = double-ended queue [OK]
- Confusing dequeue with stack or simple queue
- Thinking insertion is only at one end
- Assuming array implementation only
Solution
Step 1: Identify linked list pointers
For linked list, front and rear pointers are initialized to None to indicate empty.Step 2: Check options
class Dequeue: def __init__(self): self.front = None; self.rear = None correctly initializes front and rear as None; others use incorrect types or structures.Final Answer:
class Dequeue: def __init__(self): self.front = None; self.rear = None -> Option AQuick Check:
Linked list empty pointers = None [OK]
- Using list instead of linked list
- Initializing pointers with 0 instead of None
- Confusing front/rear with indices
dq.insert_rear(10)
dq.insert_front(20)
dq.insert_rear(30)
dq.delete_front()Print the dequeue from front to rear.
Solution
Step 1: Perform insert_rear(10) on empty dequeue
Dequeue becomes: 10 -> nullStep 2: Perform insert_front(20)
Dequeue becomes: 20 -> 10 -> nullStep 3: Perform insert_rear(30)
Dequeue becomes: 20 -> 10 -> 30 -> nullStep 4: Perform delete_front()
Remove 20 from front, dequeue becomes: 10 -> 30 -> nullFinal Answer:
10 -> 30 -> null -> Option CQuick Check:
After operations, front to rear = 10, 30 [OK]
- Forgetting delete_front removes front element
- Mixing order of insert_front and insert_rear
- Printing rear to front instead of front to rear
def delete_rear(self):
if self.rear is None:
return None
temp = self.rear
self.rear = self.rear.next
return temp.dataSolution
Step 1: Understand rear pointer movement
In linked list, rear points to last node; to delete rear, rear must move backward (to previous node).Step 2: Analyze code
Code moves rear to rear.next (forward), which is incorrect for rear deletion.Final Answer:
Incorrectly moves rear pointer forward instead of backward -> Option BQuick Check:
Rear deletion moves pointer backward [OK]
- Moving rear pointer forward instead of backward
- Not handling empty dequeue properly
- Confusing front and rear pointers
1. Insert at front
2. Insert at rear
3. Delete from front
4. Delete from rear
Which of the following is the best approach to maintain pointers for O(1) time complexity for all operations?
Solution
Step 1: Understand operation requirements
All operations must be O(1), so no traversal allowed.Step 2: Analyze pointer needs
Doubly linked list with front and rear pointers allows insert/delete at both ends in O(1).Step 3: Check options
Maintain front and rear pointers; each node has next and prev links matches this approach; others require traversal or use arrays which are not linked lists.Final Answer:
Maintain front and rear pointers; each node has next and prev links -> Option AQuick Check:
Doubly linked list + front/rear pointers = O(1) ops [OK]
- Using singly linked list causing O(n) rear operations
- Maintaining only one pointer causing traversal
- Confusing arrays with linked lists
Ready to prove it?
Pass the test and this pattern moves unknown
