Delete from Beginning of Doubly Linked List in DSA Rust - Time & Space Complexity
When we delete the first node from a doubly linked list, we want to know how the time it takes changes as the list grows.
We ask: How much work does this deletion need compared to the list size?
Analyze the time complexity of the following code snippet.
struct Node {
data: i32,
prev: Option<*mut Node>,
next: Option>,
}
fn delete_from_beginning(head: &mut Option>) {
if let Some(mut node) = head.take() {
*head = node.next.take();
if let Some(ref mut new_head) = head {
new_head.prev = None;
}
}
}
This code removes the first node of a doubly linked list and updates the head pointer.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Access and update the head node and its next pointer.
- How many times: This happens only once per deletion, no loops or recursion.
Deleting the first node always 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 the same as the list grows.
Time Complexity: O(1)
This means deleting the first node takes a fixed amount of time, no matter how large the list is.
[X] Wrong: "Deleting the first node takes longer if the list is bigger because we have to check all nodes."
[OK] Correct: We only update the head and the next node's pointers, no need to visit other nodes.
Understanding this operation's time helps you explain efficient list manipulations clearly and confidently in interviews.
"What if we changed to deleting from the end of the doubly linked list? How would the time complexity change?"
Practice
head pointer when you delete the first node from a doubly linked list?Solution
Step 1: Understand the role of
Theheadin a doubly linked listheadpointer always points to the first node in the list.Step 2: Effect of deleting the first node
When the first node is deleted, theheadmust move to the next node to keep the list accessible.Final Answer:
It moves to the next node in the list -> Option BQuick Check:
Deleting first node updates head to next node [OK]
- Not updating head pointer after deletion
- Setting head to null without checking list length
- Confusing head with tail pointer
Solution
Step 1: Analyze safe Rust deletion pattern
if let Some(mut node) = head.take() { head = node.next.take(); if let Some(ref mut new_head) = head { new_head.prev = None; } } usestake()to safely remove the first node and updatesheadandprevpointers correctly.Step 2: Check other options for errors
head = head.next; head.prev = None; tries to accesshead.nextwithout handlingOption. head = None; tail = None; deletes entire list incorrectly. head.prev = None; head = head.next; accesseshead.prevbefore updatinghead, causing error.Final Answer:
Option A code snippet -> Option AQuick Check:
Safe pointer update with take() = if let Some(mut node) = head.take() { head = node.next.take(); if let Some(ref mut new_head) = head { new_head.prev = None; } } [OK]
- Ignoring Option unwrap safety
- Not updating prev pointer of new head
- Setting head to None without checking
struct Node { val: i32, prev: Option, next: Option }
// List: 10 <-> 20 <-> 30
let mut head = Some(Box::new(Node { val: 10, prev: None, next: Some(Box::new(Node { val: 20, prev: None, next: Some(Box::new(Node { val: 30, prev: None, next: None })) })) }));
// Delete first node
if let Some(mut node) = head.take() {
head = node.next.take();
if let Some(ref mut new_head) = head {
new_head.prev = None;
}
}
// Print list
let mut current = &head;
while let Some(node) = current {
print!("{} -> ", node.val);
current = &node.next;
}
print!("null");Solution
Step 1: Initial list structure
The list starts as 10 <-> 20 <-> 30, with head at 10.Step 2: Deletion of first node
Deleting first node moves head to 20, and sets 20.prev to None.Step 3: Printing the list
Starting from new head (20), printing nodes gives 20 -> 30 -> null.Final Answer:
20 -> 30 -> null -> Option CQuick Check:
Head updated to second node, list prints from 20 [OK]
- Printing old head after deletion
- Not updating prev pointer causing broken links
- Confusing next and prev pointers
if head.is_some() {
head = head.unwrap().next;
if let Some(ref mut new_head) = head {
new_head.prev = None;
}
}Solution
Step 1: Understand unwrap() behavior on Option
Callingunwrap()onheadmoves the value out, consuming it and invalidatinghead.Step 2: Consequence of move error
Assigninghead = head.unwrap().next;causes a move error becauseheadis used after being moved.Final Answer:
Using unwrap() consumes head, causing a move error -> Option AQuick Check:
unwrap() moves value, invalidating head [OK]
- Using unwrap() causing move errors
- Not handling Option safely
- Forgetting to update prev pointer
let mut head = Some(Box::new(Node { val: 5, prev: None, next: Some(Box::new(Node { val: 15, prev: None, next: Some(Box::new(Node { val: 25, prev: None, next: Some(Box::new(Node { val: 35, prev: None, next: None })) })) })) }));
// Delete first node twice
for _ in 0..2 {
if let Some(mut node) = head.take() {
head = node.next.take();
if let Some(ref mut new_head) = head {
new_head.prev = None;
}
}
}
// Print list
let mut current = &head;
while let Some(node) = current {
print!("{} -> ", node.val);
current = &node.next;
}
print!("null");Solution
Step 1: Initial list and first deletion
List starts as 5 <-> 15 <-> 25 <-> 35. First deletion removes 5, head moves to 15.Step 2: Second deletion
Second deletion removes 15, head moves to 25, and 25.prev is set to None.Step 3: Printing final list
Starting from 25, printing nodes gives 25 -> 35 -> null.Final Answer:
25 -> 35 -> null -> Option DQuick Check:
Two deletions move head twice forward [OK]
- Not updating prev pointer after each deletion
- Deleting only once instead of twice
- Printing old head after deletions
Ready to prove it?
Pass the test and this pattern moves unknown
