Insert at Specific Position in Doubly Linked List in DSA Go - Time & Space Complexity
When inserting a new item at a specific place in a doubly linked list, we want to know how the time to do this changes as the list grows.
We ask: How much work does the computer do as the list gets bigger?
Analyze the time complexity of the following code snippet.
func InsertAtPosition(head *Node, data int, pos int) *Node {
newNode := &Node{data: data}
if pos == 1 {
newNode.next = head
if head != nil {
head.prev = newNode
}
return newNode
}
current := head
for i := 1; i < pos-1 && current != nil; i++ {
current = current.next
}
if current == nil {
return head
}
newNode.next = current.next
if current.next != nil {
current.next.prev = newNode
}
current.next = newNode
newNode.prev = current
return head
}
This code inserts a new node at a given position in a doubly linked list by moving through the list to find the right spot.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The for-loop that moves through the list nodes to reach the position before insertion.
- How many times: Up to pos - 1 times, which depends on the position where we insert.
As the list gets bigger, the time to insert depends on how far we must move to reach the position.
| Input Size (n) | Approx. Operations (steps to reach position) |
|---|---|
| 10 | Up to 10 steps |
| 100 | Up to 100 steps |
| 1000 | Up to 1000 steps |
Pattern observation: The number of steps grows roughly in direct proportion to the position number, which can be as large as the list size.
Time Complexity: O(n)
This means the time to insert grows linearly with the size of the list because we may need to walk through many nodes to find the insertion point.
[X] Wrong: "Inserting at any position in a doubly linked list always takes constant time because we can go both ways."
[OK] Correct: Even though nodes link both ways, we still must find the position by moving through nodes one by one, which takes time proportional to the position.
Understanding this helps you explain how linked lists work and why some operations are faster or slower, a key skill in many coding discussions.
"What if we kept a pointer to the tail of the list and wanted to insert near the end? How would the time complexity change?"
Practice
Solution
Step 1: Understand doubly linked list structure
Each node has pointers to both previous and next nodes, so insertion requires updating these pointers.Step 2: Insert node at specific position
To insert, update the previous node's next pointer and the next node's previous pointer to point to the new node.Final Answer:
Adjusting the previous and next pointers of neighboring nodes to include the new node -> Option BQuick Check:
Pointer updates = A [OK]
- Only updating one pointer and ignoring the other
- Not handling edge cases like insertion at head or tail
- Replacing node data instead of inserting new node
Solution
Step 1: Check struct fields for doubly linked list
A doubly linked list node must have pointers to previous and next nodes, both of type *Node.Step 2: Validate Go syntax
type Node struct { data int; prev *Node; next *Node } correctly uses pointers (*Node) for prev and next fields, matching doubly linked list requirements.Final Answer:
type Node struct { data int; prev *Node; next *Node } -> Option AQuick Check:
Pointers for prev and next = A [OK]
- Using non-pointer types for prev and next
- Omitting prev pointer for doubly linked list
- Using incorrect field types like int for pointers
Solution
Step 1: Identify position 3 in the list
Positions: 1=10, 2=20, 3=30, 4=40. Inserting at position 3 means before node with value 30.Step 2: Insert node with value 25 at position 3
New node 25 goes between 20 and 30, updating pointers accordingly.Final Answer:
10 <-> 20 <-> 25 <-> 30 <-> 40 -> Option AQuick Check:
Insert before 3rd node = D [OK]
- Inserting after the node at position instead of before
- Appending at end instead of correct position
- Confusing position indexing starting at 0
func InsertAtPosition(head *Node, data int, pos int) *Node {
newNode := &Node{data: data}
if pos == 1 {
newNode.next = head
head.prev = newNode
head = newNode
return head
}
temp := head
for i := 1; i < pos-1; i++ {
temp = temp.next
}
newNode.next = temp.next
temp.next.prev = newNode
temp.next = newNode
newNode.prev = temp
return head
}Solution
Step 1: Analyze pointer usage in loop and insertion
The loop moves temp to position before insertion. If pos is beyond list length, temp or temp.next can be nil.Step 2: Identify missing nil checks
Accessing temp.next.prev without checking if temp.next is nil can cause runtime panic.Final Answer:
No check if temp or temp.next is nil before accessing pointers -> Option CQuick Check:
Missing nil checks cause runtime errors [OK]
- Ignoring nil pointer checks causing crashes
- Incorrect pointer assignments order
- Returning wrong node pointer
Solution
Step 1: Determine correct insertion position
Position 3 means node with value 20 goes between nodes 15 (pos 2) and 25 (pos 3).Step 2: Update pointers for insertion
Set 15.next to new node 20, 20.prev to 15, 20.next to 25, and 25.prev to 20 to maintain list integrity.Final Answer:
5 <-> 15 <-> 20 <-> 25 <-> 35; update 15.next to 20, 20.prev to 15, 20.next to 25, 25.prev to 20 -> Option DQuick Check:
Insert between 15 and 25 with pointer updates = C [OK]
- Swapping nodes or inserting at wrong position
- Forgetting to update prev pointers
- Incorrectly updating next pointers causing broken links
Ready to prove it?
Pass the test and this pattern moves unknown
