Bird
Raised Fist0
DSA Rustprogramming

Delete by Value in Doubly Linked List in DSA Rust

Choose your learning style10 modes available
Mental Model
We find the node with the given value and remove it by linking its previous and next nodes directly.
Analogy: Imagine a train with cars linked together. To remove one car, you uncouple it and connect the car before it directly to the car after it.
null ← 1 ↔ 2 ↔ 3 ↔ 4 -> null
          ↑
Dry Run Walkthrough
Input: list: 1 ↔ 2 ↔ 3 ↔ 4, delete value 3
Goal: Remove the node containing value 3 and keep the list connected
Step 1: start at head node with value 1
null ← [1] ↔ 2 ↔ 3 ↔ 4 -> null
Why: We begin searching from the start to find the node to delete
Step 2: move to next node with value 2
null ← 1 ↔ [2] ↔ 3 ↔ 4 -> null
Why: Value 2 is not the target, continue searching
Step 3: move to next node with value 3
null ← 1 ↔ 2 ↔ [3] ↔ 4 -> null
Why: Found the node with value 3 to delete
Step 4: unlink node 3 by linking node 2's next to node 4 and node 4's prev to node 2
null ← 1 ↔ 2 ↔ 4 -> null
Why: Removing node 3 keeps the list connected without it
Result:
null ← 1 ↔ 2 ↔ 4 -> null
Annotated Code
DSA Rust
use std::rc::Rc;
use std::cell::RefCell;

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

impl Node {
    fn new(val: i32) -> Rc<RefCell<Node>> {
        Rc::new(RefCell::new(Node { val, prev: None, next: None }))
    }
}

struct DoublyLinkedList {
    head: Option<Rc<RefCell<Node>>>,
    tail: Option<Rc<RefCell<Node>>>,
}

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

    fn append(&mut self, val: i32) {
        let new_node = Node::new(val);
        match self.tail.take() {
            Some(old_tail) => {
                old_tail.borrow_mut().next = Some(new_node.clone());
                new_node.borrow_mut().prev = Some(old_tail);
                self.tail = Some(new_node);
            }
            None => {
                self.head = Some(new_node.clone());
                self.tail = Some(new_node);
            }
        }
    }

    fn delete_by_value(&mut self, val: i32) {
        let mut current = self.head.clone();
        while let Some(node) = current {
            if node.borrow().val == val {
                let prev = node.borrow_mut().prev.take();
                let next = node.borrow_mut().next.take();

                if let Some(prev_node) = prev.clone() {
                    prev_node.borrow_mut().next = next.clone();
                } else {
                    self.head = next.clone();
                }

                if let Some(next_node) = next.clone() {
                    next_node.borrow_mut().prev = prev.clone();
                } else {
                    self.tail = prev.clone();
                }

                break;
            }
            current = node.borrow().next.clone();
        }
    }

    fn print_list(&self) {
        let mut current = self.head.clone();
        let mut result = String::new();
        while let Some(node) = current {
            result.push_str(&format!("{}", node.borrow().val));
            current = node.borrow().next.clone();
            if current.is_some() {
                result.push_str(" ↔ ");
            }
        }
        println!("{}", result);
    }
}

fn main() {
    let mut dll = DoublyLinkedList::new();
    dll.append(1);
    dll.append(2);
    dll.append(3);
    dll.append(4);
    dll.delete_by_value(3);
    dll.print_list();
}
while let Some(node) = current {
iterate through nodes to find target value
if node.borrow().val == val {
check if current node has the value to delete
let prev = node.borrow_mut().prev.take();
temporarily remove prev link from current node
let next = node.borrow_mut().next.take();
temporarily remove next link from current node
if let Some(prev_node) = prev.clone() { prev_node.borrow_mut().next = next.clone(); } else { self.head = next.clone(); }
link previous node's next to current node's next or update head if deleting first node
if let Some(next_node) = next.clone() { next_node.borrow_mut().prev = prev.clone(); } else { self.tail = prev.clone(); }
link next node's prev to current node's prev or update tail if deleting last node
break;
stop after deleting the first matching node
current = node.borrow().next.clone();
move to next node if current node is not target
OutputSuccess
1 ↔ 2 ↔ 4
Complexity Analysis
Time: O(n) because we may need to check each node once to find the value
Space: O(1) because deletion is done by changing pointers without extra storage
vs Alternative: Compared to rebuilding the list without the value (O(n) time and space), this pointer update is more efficient in space
Edge Cases
empty list
nothing happens, list remains empty
DSA Rust
while let Some(node) = current {
value not found
list remains unchanged after full traversal
DSA Rust
while let Some(node) = current {
delete head node
head pointer updates to next node
DSA Rust
else { self.head = next.clone(); }
delete tail node
tail pointer updates to previous node
DSA Rust
else { self.tail = prev.clone(); }
When to Use This Pattern
When you need to remove a specific value from a doubly linked list, use the delete by value pattern to unlink the node by adjusting its neighbors' pointers.
Common Mistakes
Mistake: Not updating the head or tail pointers when deleting the first or last node
Fix: Explicitly check if prev or next is None and update head or tail accordingly
Mistake: Not breaking after deleting the node, causing errors or multiple deletions
Fix: Add a break statement immediately after deletion to stop traversal
Summary
Deletes the first node with the given value by reconnecting its neighbors.
Use when you want to remove a specific value from a doubly linked list efficiently.
The key is to update the previous node's next and next node's prev pointers to skip the deleted node.

Practice

(1/5)
1. What is the main purpose of the delete_by_value function in a doubly linked list?
easy
A. To remove the first node that contains the given value
B. To add a new node with the given value at the end
C. To reverse the entire doubly linked list
D. To count how many nodes have the given value

Solution

  1. Step 1: Understand the function's goal

    The delete_by_value function searches for a node with the specified value.
  2. Step 2: Identify the action performed

    Once found, it removes that node from the doubly linked list, adjusting pointers accordingly.
  3. Final Answer:

    To remove the first node that contains the given value -> Option A
  4. Quick Check:

    Delete by value means remove matching node [OK]
Hint: Delete by value removes first matching node [OK]
Common Mistakes:
  • Confusing delete with insert
  • Thinking it reverses the list
  • Assuming it counts nodes
2. Which Rust syntax correctly declares a mutable doubly linked list node struct with prev and next pointers?
easy
A. struct Node { value: i32, prev: Option>, next: Option> }
B. struct Node { value: i32, prev: Option<*mut Node>, next: Option<*mut Node> }
C. struct Node { value: i32, prev: Option, next: Option }
D. struct Node { value: i32, prev: Option<Node>, next: Option<Node> }

Solution

  1. Step 1: Understand Rust pointer types for doubly linked lists

    Rust needs shared ownership and mutability for prev and next pointers, so Rc is used.
  2. Step 2: Check options for correct pointer usage

    struct Node { value: i32, prev: Option>, next: Option> } uses Option> which allows multiple owners and interior mutability, suitable for doubly linked lists.
  3. Final Answer:

    struct Node { value: i32, prev: Option>, next: Option> } -> Option A
  4. Quick Check:

    Use Rc and RefCell for mutable shared nodes [OK]
Hint: Use Rc for mutable shared pointers [OK]
Common Mistakes:
  • Using Box for both prev and next causes ownership issues
  • Using raw pointers without unsafe code
  • Using direct Node type causing infinite size
3. Given the following Rust code snippet for deleting a value from a doubly linked list, what will be the printed list after deleting value 3?
let mut list = DoublyLinkedList::new();
list.push_back(1);
list.push_back(3);
list.push_back(5);
list.delete_by_value(3);
list.print();
medium
A. 1 -> 3 -> 5 -> null
B. 1 -> 5 -> null
C. 3 -> 5 -> null
D. 1 -> null

Solution

  1. Step 1: Understand initial list state

    After pushing 1, 3, 5, the list is 1 -> 3 -> 5 -> null.
  2. Step 2: Delete node with value 3

    The delete_by_value(3) removes the node containing 3, linking 1 directly to 5.
  3. Final Answer:

    1 -> 5 -> null -> Option B
  4. Quick Check:

    Deleted 3, list is 1 -> 5 [OK]
Hint: Deleting value removes that node, links neighbors [OK]
Common Mistakes:
  • Not updating prev and next pointers correctly
  • Deleting wrong node or none
  • Printing old list without deletion
4. Consider this Rust function snippet for deleting a node by value in a doubly linked list:
fn delete_by_value(&mut self, val: i32) {
    let mut current = self.head.clone();
    while let Some(node) = current {
        if node.borrow().value == val {
            // deletion logic here
            break;
        }
        current = node.borrow().next.clone();
    }
}
What is a common bug if the deletion logic forgets to update the prev pointer of the next node?
medium
A. The list will reverse automatically
B. The head pointer will always point to null
C. The deleted node will remain in memory forever
D. The list may become corrupted with dangling backward links

Solution

  1. Step 1: Identify pointer updates needed on deletion

    When deleting a node, the previous node's next and the next node's prev pointers must be updated to bypass the deleted node.
  2. Step 2: Consequence of missing prev update

    If the next node's prev pointer is not updated, it still points to the deleted node, causing backward links to be incorrect and list corruption.
  3. Final Answer:

    The list may become corrupted with dangling backward links -> Option D
  4. Quick Check:

    Missing prev update breaks backward links [OK]
Hint: Always update both prev and next pointers when deleting [OK]
Common Mistakes:
  • Updating only next pointer
  • Not breaking loop after deletion
  • Forgetting to handle head or tail cases
5. You have a doubly linked list with nodes: 2 -> 4 -> 6 -> 4 -> 8 -> null. You want to delete all nodes with value 4 using a delete_by_value function that removes only the first matching node per call. How many times must you call delete_by_value(4) to remove all 4s, and what will the final list look like?
hard
A. Call once; final list: 2 -> 4 -> 6 -> 8 -> null
B. Call once; final list: 2 -> 6 -> 4 -> 8 -> null
C. Call twice; final list: 2 -> 6 -> 8 -> null
D. Call twice; final list: 2 -> 4 -> 6 -> 8 -> null

Solution

  1. Step 1: Understand deletion per call

    Each call to delete_by_value(4) removes only the first node with value 4.
  2. Step 2: Count how many 4s and calls needed

    There are two nodes with value 4. So, two calls are needed to remove both.
  3. Step 3: Final list after deletions

    After two deletions, the list becomes 2 -> 6 -> 8 -> null.
  4. Final Answer:

    Call twice; final list: 2 -> 6 -> 8 -> null -> Option C
  5. Quick Check:

    Two 4s, two deletes, final list without 4s [OK]
Hint: Delete one node per call; repeat to remove all matches [OK]
Common Mistakes:
  • Assuming one call removes all matching nodes
  • Not updating pointers after each deletion
  • Confusing node values or order
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