Bird
Raised Fist0
DSA Rustprogramming

Delete from Beginning of Doubly Linked List in DSA Rust

Choose your learning style10 modes available
Mental Model
To remove the first item, we move the start pointer to the next item and fix links so the list stays connected.
Analogy: Imagine a train where the first carriage is detached and the second carriage becomes the new front, with no loose connections.
null ← 1 ↔ 2 ↔ 3 -> null
↑head
Dry Run Walkthrough
Input: list: 1 ↔ 2 ↔ 3, delete from beginning
Goal: Remove the first node and update the list so it starts at the second node
Step 1: head moves from node 1 to node 2
null ← 1 ↔ 2 ↔ 3 -> null
     ↑head
Why: We want the second node to become the new start
Step 2: set previous pointer of new head (node 2) to null
null ← 2 ↔ 3 -> null
↑head
Why: Disconnect old first node so list starts cleanly at node 2
Step 3: old first node (1) is removed from memory
null ← 2 ↔ 3 -> null
↑head
Why: Complete removal of first node to avoid memory leaks
Result:
null ← 2 ↔ 3 -> null
↑head
Annotated Code
DSA Rust
use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct Node {
    data: i32,
    prev: Option<Rc<RefCell<Node>>>,
    next: Option<Rc<RefCell<Node>>>,
}

#[derive(Debug)]
struct DoublyLinkedList {
    head: Option<Rc<RefCell<Node>>>,
}

impl DoublyLinkedList {
    fn new() -> Self {
        DoublyLinkedList { head: None }
    }

    fn append(&mut self, data: i32) {
        let new_node = Rc::new(RefCell::new(Node { data, prev: None, next: None }));
        match self.head.take() {
            None => {
                self.head = Some(new_node);
            }
            Some(old_head) => {
                let mut tail = old_head.clone();
                while let Some(next) = tail.borrow().next.clone() {
                    tail = next;
                }
                tail.borrow_mut().next = Some(new_node.clone());
                new_node.borrow_mut().prev = Some(tail);
                self.head = Some(old_head);
            }
        }
    }

    fn delete_from_beginning(&mut self) {
        if let Some(old_head) = self.head.take() {
            let next_node = old_head.borrow_mut().next.take();
            if let Some(next) = &next_node {
                next.borrow_mut().prev = None; // disconnect prev pointer
            }
            self.head = next_node; // move head to next node
            // old_head drops here and is removed if no other references
        }
    }

    fn print_list(&self) {
        let mut curr = self.head.clone();
        while let Some(node) = curr {
            print!("{}", node.borrow().data);
            curr = node.borrow().next.clone();
            if curr.is_some() {
                print!(" ↔ ");
            }
        }
        println!(" -> null");
    }
}

fn main() {
    let mut dll = DoublyLinkedList::new();
    dll.append(1);
    dll.append(2);
    dll.append(3);
    dll.print_list();
    dll.delete_from_beginning();
    dll.print_list();
}
if let Some(old_head) = self.head.take() {
check if list is not empty and take ownership of current head
let next_node = old_head.borrow_mut().next.take();
get the node after head and disconnect old head's next pointer
if let Some(next) = &next_node { next.borrow_mut().prev = None; }
set new head's prev pointer to None to disconnect old head
self.head = next_node;
update head pointer to new first node
OutputSuccess
1 ↔ 2 ↔ 3 -> null 2 ↔ 3 -> null
Complexity Analysis
Time: O(1) because we only update pointers at the start without traversal
Space: O(1) since no extra space is needed beyond pointer updates
vs Alternative: Compared to deleting from the end which requires traversal O(n), deleting from beginning is faster and simpler
Edge Cases
empty list
delete does nothing, list remains empty
DSA Rust
if let Some(old_head) = self.head.take() {
single element list
head becomes None, list becomes empty
DSA Rust
let next_node = old_head.borrow_mut().next.take();
When to Use This Pattern
When you need to remove the first item quickly from a doubly linked list, use delete from beginning to update head and pointers efficiently.
Common Mistakes
Mistake: Not setting the new head's prev pointer to None after deletion
Fix: Always set new head's prev to None to avoid dangling backward links
Mistake: Not handling empty list case causing panic
Fix: Check if head exists before deleting to avoid errors
Summary
Removes the first node from a doubly linked list by moving the head pointer and fixing links.
Use when you want to delete the front element quickly without traversing the list.
The key is to update the head and disconnect the old first node cleanly to keep list integrity.

Practice

(1/5)
1. What happens to the head pointer when you delete the first node from a doubly linked list?
easy
A. It points to the last node
B. It moves to the next node in the list
C. It becomes null immediately
D. It remains unchanged

Solution

  1. Step 1: Understand the role of head in a doubly linked list

    The head pointer always points to the first node in the list.
  2. Step 2: Effect of deleting the first node

    When the first node is deleted, the head must move to the next node to keep the list accessible.
  3. Final Answer:

    It moves to the next node in the list -> Option B
  4. Quick Check:

    Deleting first node updates head to next node [OK]
Hint: Head always points to first node; update it after deletion [OK]
Common Mistakes:
  • Not updating head pointer after deletion
  • Setting head to null without checking list length
  • Confusing head with tail pointer
2. Which of the following Rust code snippets correctly deletes the first node from a doubly linked list?
easy
A. if let Some(mut node) = head.take() { head = node.next.take(); if let Some(ref mut new_head) = head { new_head.prev = None; } }
B. head = head.next; head.prev = None;
C. head = None; tail = None;
D. head.prev = None; head = head.next;

Solution

  1. 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; } } uses take() to safely remove the first node and updates head and prev pointers correctly.
  2. Step 2: Check other options for errors

    head = head.next; head.prev = None; tries to access head.next without handling Option. head = None; tail = None; deletes entire list incorrectly. head.prev = None; head = head.next; accesses head.prev before updating head, causing error.
  3. Final Answer:

    Option A code snippet -> Option A
  4. Quick 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]
Hint: Use take() to safely remove and update pointers [OK]
Common Mistakes:
  • Ignoring Option unwrap safety
  • Not updating prev pointer of new head
  • Setting head to None without checking
3. Given the following Rust code for a doubly linked list deletion from beginning, what will be the printed list after deletion?
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");
medium
A. null
B. 10 -> 20 -> 30 -> null
C. 20 -> 30 -> null
D. 30 -> null

Solution

  1. Step 1: Initial list structure

    The list starts as 10 <-> 20 <-> 30, with head at 10.
  2. Step 2: Deletion of first node

    Deleting first node moves head to 20, and sets 20.prev to None.
  3. Step 3: Printing the list

    Starting from new head (20), printing nodes gives 20 -> 30 -> null.
  4. Final Answer:

    20 -> 30 -> null -> Option C
  5. Quick Check:

    Head updated to second node, list prints from 20 [OK]
Hint: After deletion, head points to second node; print from there [OK]
Common Mistakes:
  • Printing old head after deletion
  • Not updating prev pointer causing broken links
  • Confusing next and prev pointers
4. Identify the error in this Rust code snippet that tries to delete the first node from a doubly linked list:
if head.is_some() {
  head = head.unwrap().next;
  if let Some(ref mut new_head) = head {
    new_head.prev = None;
  }
}
medium
A. Using unwrap() consumes head, causing a move error
B. Not setting new_head.prev to None
C. Missing check if head is None before deletion
D. Incorrectly updating tail pointer

Solution

  1. Step 1: Understand unwrap() behavior on Option

    Calling unwrap() on head moves the value out, consuming it and invalidating head.
  2. Step 2: Consequence of move error

    Assigning head = head.unwrap().next; causes a move error because head is used after being moved.
  3. Final Answer:

    Using unwrap() consumes head, causing a move error -> Option A
  4. Quick Check:

    unwrap() moves value, invalidating head [OK]
Hint: Avoid unwrap() on Option when reassigning; use take() [OK]
Common Mistakes:
  • Using unwrap() causing move errors
  • Not handling Option safely
  • Forgetting to update prev pointer
5. You have a doubly linked list with nodes: 5 <-> 15 <-> 25 <-> 35. After deleting the first node twice using correct Rust code, what will be the final list printed?
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");
hard
A. 15 -> 25 -> 35 -> null
B. null
C. 35 -> null
D. 25 -> 35 -> null

Solution

  1. Step 1: Initial list and first deletion

    List starts as 5 <-> 15 <-> 25 <-> 35. First deletion removes 5, head moves to 15.
  2. Step 2: Second deletion

    Second deletion removes 15, head moves to 25, and 25.prev is set to None.
  3. Step 3: Printing final list

    Starting from 25, printing nodes gives 25 -> 35 -> null.
  4. Final Answer:

    25 -> 35 -> null -> Option D
  5. Quick Check:

    Two deletions move head twice forward [OK]
Hint: Each deletion moves head to next node; repeat for multiple deletions [OK]
Common Mistakes:
  • Not updating prev pointer after each deletion
  • Deleting only once instead of twice
  • Printing old head after deletions
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard