Delete from End of Doubly Linked List in DSA Go - Time & Space Complexity
We want to understand how long it takes to remove the last item from a doubly linked list.
How does the time needed change as the list grows bigger?
Analyze the time complexity of the following code snippet.
type Node struct {
data int
prev *Node
next *Node
}
type DoublyLinkedList struct {
head *Node
tail *Node
}
func (dll *DoublyLinkedList) DeleteFromEnd() {
if dll.tail == nil {
return
}
if dll.tail.prev == nil {
dll.head = nil
dll.tail = nil
return
}
dll.tail = dll.tail.prev
dll.tail.next = nil
}
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 only once per call, no loops or recursion.
Removing the last node takes the same steps no matter how big the list is.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 5-6 steps |
| 100 | 5-6 steps |
| 1000 | 5-6 steps |
Pattern observation: The number of steps stays about the same regardless of list size.
Time Complexity: O(1)
This means deleting from the end takes a constant amount of time no matter how large the list is.
[X] Wrong: "Deleting from the end takes longer as the list grows because you have to find the last node."
[OK] Correct: The doubly linked list keeps a direct pointer to the last node (tail), so no searching is needed.
Knowing that deleting from the end is quick helps you choose the right data structure for fast removals.
"What if we did not keep a tail pointer? How would the time complexity change?"
Practice
Solution
Step 1: Understand the tail pointer role
The tail pointer points to the last node in the doubly linked list.Step 2: Effect of deleting the last node
When the last node is deleted, the tail pointer must move to the previous node to remain valid.Final Answer:
The tail pointer moves to the previous node. -> Option DQuick Check:
Tail moves backward after deletion [OK]
- Not updating tail pointer after deletion
- Setting tail to next node which is null
- Leaving tail pointer unchanged
dll?Solution
Step 1: Identify correct pointer updates for tail deletion
Deleting last node means tail moves to previous node and new tail's next is nil.Step 2: Check code correctness
dll.tail = dll.tail.prev; dll.tail.next = nil sets tail to previous node and sets tail.next to nil, which is correct.Final Answer:
dll.tail = dll.tail.prev; dll.tail.next = nil -> Option BQuick Check:
Tail moves back, next cleared [OK]
- Moving tail forward instead of backward
- Not setting tail.next to nil
- Changing head pointer instead of tail
package main
import "fmt"
type Node struct {
data int
prev *Node
next *Node
;
}
type DoublyLinkedList struct {
head *Node
tail *Node
;
}
func (dll *DoublyLinkedList) DeleteFromEnd() {
if dll.tail == nil {
return
}
if dll.tail.prev == nil {
dll.head = nil
dll.tail = nil
return
}
dll.tail = dll.tail.prev
dll.tail.next = nil
}
func (dll *DoublyLinkedList) Print() {
current := dll.head
for current != nil {
fmt.Print(current.data, " -> ")
current = current.next
}
fmt.Println("null")
}
func main() {
n1 := &Node{data: 1}
n2 := &Node{data: 2}
n3 := &Node{data: 3}
n1.next = n2
n2.prev = n1
n2.next = n3
n3.prev = n2
dll := DoublyLinkedList{head: n1, tail: n3}
dll.DeleteFromEnd()
dll.Print()
}Solution
Step 1: Initial list and deletion
List is 1 -> 2 -> 3 -> null. DeleteFromEnd removes last node (3).Step 2: After deletion
Tail moves to node with data 2, tail.next set to nil. List becomes 1 -> 2 -> null.Final Answer:
1 -> 2 -> null -> Option AQuick Check:
Last node removed, tail updated [OK]
- Not updating tail.next to nil causing dangling pointer
- Printing old list without deletion
- Confusing head and tail pointers
func (dll *DoublyLinkedList) DeleteFromEnd() {
if dll.tail == nil {
return
}
dll.tail = dll.tail.prev
dll.tail.next = nil
}Solution
Step 1: Analyze edge case for single node list
If list has one node, tail.prev is nil, so after tail = tail.prev, tail is nil.Step 2: Cause of panic
Next line tries to access tail.next but tail is nil, causing runtime panic.Final Answer:
It does not handle the case when the list has only one node. -> Option CQuick Check:
Missing single-node check causes nil pointer panic [OK]
- Not checking if tail.prev is nil before access
- Assuming list always has multiple nodes
- Ignoring head pointer update when list becomes empty
Solution
Step 1: Initial list and first deletion
List is 10 <-> 20 <-> 30 <-> 40. First deletion removes 40, list becomes 10 <-> 20 <-> 30.Step 2: Second deletion
Second deletion removes 30, list becomes 10 <-> 20.Step 3: Print list
Printing from head: 10 -> 20 -> null.Final Answer:
10 -> 20 -> null -> Option AQuick Check:
Two deletions remove last two nodes [OK]
- Deleting only once instead of twice
- Confusing node values after deletion
- Not updating tail pointer correctly
Ready to prove it?
Pass the test and this pattern moves unknown
