Bird
Raised Fist0
DSA Rustprogramming

Insert at Specific Position in Doubly Linked List in DSA Rust

Choose your learning style10 modes available
Mental Model
A doubly linked list lets you move forward and backward through nodes. To insert at a position, you find that spot and link the new node between its neighbors.
Analogy: Imagine a train with cars connected front and back. To add a new car in the middle, you disconnect the two cars at that spot and connect the new car between them.
null ← 1 ↔ 2 ↔ 3 -> null
           ↑ (head)
Dry Run Walkthrough
Input: list: 1 ↔ 2 ↔ 3, insert value 4 at position 2 (0-based index)
Goal: Insert the value 4 between nodes 2 and 3, so the list becomes 1 ↔ 2 ↔ 4 ↔ 3
Step 1: start at head node 1
null ← [1] ↔ 2 ↔ 3 -> null
Why: We begin traversal from the head to find the insertion point
Step 2: move to node 2 (position 1)
null ← 1 ↔ [2] ↔ 3 -> null
Why: We advance to the node before the insertion position
Step 3: create new node 4 and link it after node 2
null ← 1 ↔ 2 ↔ [4] ↔ 3 -> null
Why: Insert new node by adjusting pointers between node 2 and node 3
Result:
null ← 1 ↔ 2 ↔ 4 ↔ 3 -> 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>>>,
}

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

    fn append(&mut self, val: i32) {
        let new_node = Node::new(val);
        match self.head.clone() {
            None => self.head = Some(new_node),
            Some(head) => {
                let mut tail = 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);
            }
        }
    }

    fn insert_at_position(&mut self, val: i32, pos: usize) {
        let new_node = Node::new(val);
        if pos == 0 {
            if let Some(head) = self.head.take() {
                new_node.borrow_mut().next = Some(head.clone());
                head.borrow_mut().prev = Some(new_node.clone());
            }
            self.head = Some(new_node);
            return;
        }

        let mut current = match self.head.clone() {
            None => return, // empty list, do nothing
            Some(node) => node,
        };

        let mut index = 0;
        while index < pos - 1 {
            let next = current.borrow().next.clone();
            match next {
                Some(next_node) => current = next_node,
                None => return, // position out of bounds
            }
            index += 1;
        }

        let next_node = current.borrow().next.clone();
        current.borrow_mut().next = Some(new_node.clone());
        new_node.borrow_mut().prev = Some(current.clone());
        new_node.borrow_mut().next = next_node.clone();
        if let Some(next) = next_node {
            next.borrow_mut().prev = Some(new_node.clone());
        }
    }

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

fn main() {
    let mut dll = DoublyLinkedList::new();
    dll.append(1);
    dll.append(2);
    dll.append(3);
    dll.insert_at_position(4, 2);
    dll.print_list();
}
while let Some(next) = tail.borrow().next.clone() { tail = next; }
advance tail to last node to append new node
tail.borrow_mut().next = Some(new_node.clone()); new_node.borrow_mut().prev = Some(tail);
link new node after tail for append
if pos == 0 { if let Some(head) = self.head.take() { new_node.borrow_mut().next = Some(head.clone()); head.borrow_mut().prev = Some(new_node.clone()); } self.head = Some(new_node); return; }
handle insertion at head by adjusting pointers
while index < pos - 1 { let next = current.borrow().next.clone(); match next { Some(next_node) => current = next_node, None => return, } index += 1; }
traverse to node before insertion position
let next_node = current.borrow().next.clone(); current.borrow_mut().next = Some(new_node.clone()); new_node.borrow_mut().prev = Some(current.clone()); new_node.borrow_mut().next = next_node.clone(); if let Some(next) = next_node { next.borrow_mut().prev = Some(new_node.clone()); }
insert new node by linking it between current and next_node
OutputSuccess
1 ↔ 2 ↔ 4 ↔ 3
Complexity Analysis
Time: O(n) because we may need to traverse up to n nodes to reach the insertion position
Space: O(1) because insertion uses a fixed amount of extra space for the new node
vs Alternative: Compared to arrays, doubly linked lists allow insertion without shifting elements, making insertion O(1) once position is found, but finding position is O(n)
Edge Cases
empty list
inserting at position 0 sets new node as head; other positions do nothing
DSA Rust
if pos == 0 { ... } and match self.head.clone() { None => return }
insert at head (position 0)
new node becomes new head, old head's prev pointer updated
DSA Rust
if pos == 0 { ... }
position out of bounds (greater than list length)
insertion is ignored, list remains unchanged
DSA Rust
match next { None => return }
When to Use This Pattern
When you need to insert or remove elements at any position in a list and want efficient backward and forward traversal, use a doubly linked list insertion pattern.
Common Mistakes
Mistake: Not updating the prev pointer of the node after the inserted node
Fix: Always update the next node's prev pointer to point back to the new node
Mistake: Not handling insertion at position 0 (head) separately
Fix: Add a special case to update head pointer and prev of old head
Summary
Inserts a new node at a given position in a doubly linked list by adjusting next and prev pointers.
Use when you want to add elements anywhere in a doubly linked list efficiently.
The key is to link the new node between its previous and next nodes, updating all pointers carefully.

Practice

(1/5)
1. What does inserting at a specific position in a doubly linked list involve?
easy
A. Adjusting the previous and next pointers of nodes around the position
B. Only changing the head pointer of the list
C. Reversing the entire list before insertion
D. Deleting the node at that position first

Solution

  1. Step 1: Understand doubly linked list node connections

    Each node has pointers to both previous and next nodes, so insertion requires updating these pointers around the insertion point.
  2. Step 2: Adjust pointers for insertion

    To insert at a specific position, update the previous node's next pointer and the next node's previous pointer to point to the new node.
  3. Final Answer:

    Adjusting the previous and next pointers of nodes around the position -> Option A
  4. Quick Check:

    Pointer updates = Adjust previous and next pointers [OK]
Hint: Remember: update both previous and next pointers [OK]
Common Mistakes:
  • Only updating one pointer and ignoring the other
  • Not handling edge cases like inserting at head or tail
  • Forgetting to update the new node's pointers
2. Which Rust code snippet correctly declares a doubly linked list node struct for insertion?
easy
A. struct Node { data: i32, prev: Option>, next: Option> }
B. struct Node { data: i32, prev: Option<*mut Node>, next: Option<*mut Node> }
C. struct Node { data: i32, prev: Option, next: Option }
D. struct Node { data: i32, prev: Box<Node>, next: Box<Node> }

Solution

  1. Step 1: Understand Rust ownership for doubly linked lists

    Doubly linked lists require shared ownership and mutability, so Rc is used to allow multiple owners and interior mutability.
  2. Step 2: Identify correct pointer types

    Option> allows optional pointers with shared ownership and mutability, suitable for prev and next pointers.
  3. Final Answer:

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

    Use Rc<RefCell> for shared mutable pointers [OK]
Hint: Use Rc<RefCell> for doubly linked list nodes in Rust [OK]
Common Mistakes:
  • Using Box without Rc causes ownership errors
  • Using raw pointers without unsafe code
  • Not wrapping pointers in Option for nullable links
3. Given this Rust code snippet inserting a node at position 2 in a doubly linked list with nodes 1 <-> 3, what is the list after insertion of node 2?
medium
A. 3 <-> 2 <-> 1
B. 2 <-> 1 <-> 3
C. 1 <-> 2 <-> 3
D. 1 <-> 3 <-> 2

Solution

  1. Step 1: Identify insertion position and existing list

    The list is 1 <-> 3, and we insert node 2 at position 2, meaning after node 1 and before node 3.
  2. Step 2: Insert node 2 between nodes 1 and 3

    Update node 1's next to node 2, node 2's prev to node 1, node 2's next to node 3, and node 3's prev to node 2.
  3. Final Answer:

    1 <-> 2 <-> 3 -> Option C
  4. Quick Check:

    Insert at position 2 places node 2 between 1 and 3 [OK]
Hint: Insert node between previous and next nodes at position [OK]
Common Mistakes:
  • Inserting node at wrong position
  • Not updating both prev and next pointers
  • Confusing position indexing starting at 0 or 1
4. In this Rust code for inserting at position 3, the program panics with 'index out of bounds'. What is the likely cause?
medium
A. The new node's pointers are not initialized
B. The list has fewer than 2 nodes, so position 3 is invalid
C. The head pointer is null
D. The insertion code forgets to update the tail pointer

Solution

  1. Step 1: Analyze the error message and position

    The panic 'index out of bounds' means the code tried to access a position beyond the list length. Position 3 requires at least 2 nodes before it.
  2. Step 2: Check list length against insertion position

    If the list has fewer than 2 nodes, position 3 is invalid, causing the panic.
  3. Final Answer:

    The list has fewer than 2 nodes, so position 3 is invalid -> Option B
  4. Quick Check:

    Position > list length causes index out of bounds [OK]
Hint: Check list length before inserting at position [OK]
Common Mistakes:
  • Not validating position against list length
  • Assuming tail pointer updates fix index errors
  • Ignoring null or empty list cases
5. You have a doubly linked list with nodes 10 <-> 20 <-> 30. Write the steps to insert a node with value 25 at position 3 and show the final list.
hard
A. 25 <-> 10 <-> 20 <-> 30
B. 10 <-> 25 <-> 20 <-> 30
C. 10 <-> 20 <-> 30 <-> 25
D. 10 <-> 20 <-> 25 <-> 30

Solution

  1. Step 1: Locate position 3 in the list

    Position 3 means the new node goes after the second node (20) and before the third node (30).
  2. Step 2: Update pointers to insert node 25

    Set node 20's next to new node 25, new node 25's prev to node 20, new node 25's next to node 30, and node 30's prev to new node 25.
  3. Final Answer:

    10 <-> 20 <-> 25 <-> 30 -> Option D
  4. Quick Check:

    Insert at position 3 places 25 between 20 and 30 [OK]
Hint: Insert between nodes at position - 1 and position [OK]
Common Mistakes:
  • Inserting node at wrong position
  • Not updating both prev and next pointers correctly
  • Confusing position indexing starting at 0 or 1
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