Delete from End of Doubly Linked List in DSA Python - Time & Space Complexity
When we delete a node from the end of a doubly linked list, we want to know how the time it takes changes as the list grows.
We ask: How many steps does it take to remove the last node when the list size increases?
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
self.tail = None
def delete_from_end(self):
if not self.tail:
return # List is empty
if self.head == self.tail:
self.head = None
self.tail = None
else:
self.tail = self.tail.prev
self.tail.next = None
This code removes the last node from a doubly linked list by updating pointers.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Accessing the tail node and updating pointers.
- How many times: This happens once per deletion, no loops or recursion involved.
Deleting from the end always takes the same number of steps, no matter how big the list is.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 5 steps |
| 100 | 5 steps |
| 1000 | 5 steps |
Pattern observation: The number of steps stays the same as the list grows.
Time Complexity: O(1)
This means deleting from the end takes a constant amount of time regardless of list size.
[X] Wrong: "Deleting from the end takes longer as the list grows because we have to find the last node."
[OK] Correct: The doubly linked list keeps a direct reference to the last node (tail), so no searching is needed.
Knowing that deleting from the end of a doubly linked list is quick shows you understand how pointers help save time in data structures.
"What if we did not keep a tail pointer? How would the time complexity of deleting from the end change?"
Practice
Solution
Step 1: Identify the last node and update pointers
In a doubly linked list, the last node is the tail node which has its next pointer as null. Deleting the last node means updating the tail pointer to the previous node and setting its next pointer to null. The second last node becomes the new tail.Final Answer:
The last node is removed and the second last node becomes the new tail. -> Option AQuick Check:
Delete end node = tail moves back [OK]
- Removing the head instead of the tail
- Not updating the tail pointer
- Leaving the next pointer of new tail non-null
Solution
Step 1: Move tail pointer and update next
To delete the last node, move tail to tail.prev (second last node), then set tail.next to None to mark the end of the list.Final Answer:
tail = tail.prev; tail.next = None -> Option DQuick Check:
Correct tail update syntax = tail = tail.prev; tail.next = None [OK]
- Moving tail forward instead of backward
- Setting prev pointer instead of next
- Changing head pointer instead of tail
Initial list: 10 <-> 20 <-> 30 <-> 40 <-> None Operation: Delete from endWhat is the resulting list?
Solution
Step 1: Identify last node and update
The last node is 40, which is removed by setting node 30's next pointer to None, making it the new tail. Result: 10 <-> 20 <-> 30 <-> None.Final Answer:
10 <-> 20 <-> 30 <-> None -> Option BQuick Check:
Last node 40 removed, tail is 30 [OK]
- Not updating the next pointer of new tail
- Removing the wrong node (head or middle)
- Leaving dangling pointers causing errors
if tail is None:
print('List is empty')
else:
tail = tail.prev
tail.next = None
What error can occur when this code runs on a list with only one node?Solution
Step 1: Trace execution for single node
For one node, tail.prev is None, so tail = None, then tail.next = None accesses None.next causing AttributeError.Final Answer:
AttributeError because tail.prev is None and tail.next is accessed -> Option AQuick Check:
Single node delete needs extra check [OK]
- Not handling single node list case
- Assuming tail.prev always exists
- Ignoring NoneType attribute errors
Solution
Step 1: Perform two deletions
Initial: 5 <-> 15 <-> 25 <-> 35 <-> 45. First delete removes 45 (tail=35). Second delete removes 35 (tail=25). Final: 5 <-> 15 <-> 25 <-> None.Final Answer:
5 <-> 15 <-> 25 <-> None -> Option CQuick Check:
Two deletes remove last two nodes [OK]
- Deleting only one node instead of two
- Not updating tail pointer after each delete
- Confusing head and tail nodes
Ready to prove it?
Pass the test and this pattern moves unknown
