Doubly Linked List Structure and Node Design in DSA Go - Time & Space Complexity
We want to understand how the time to create and link nodes in a doubly linked list grows as we add more nodes.
How does the work change when the list gets bigger?
Analyze the time complexity of the following code snippet.
type Node struct {
data int
prev *Node
next *Node
}
type DoublyLinkedList struct {
head *Node
tail *Node
}
func (dll *DoublyLinkedList) Append(value int) {
newNode := &Node{data: value}
if dll.head == nil {
dll.head = newNode
dll.tail = newNode
return
}
dll.tail.next = newNode
newNode.prev = dll.tail
dll.tail = newNode
}
This code creates nodes and links them to form a doubly linked list by adding new nodes at the end.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Creating a new node and linking it to the tail.
- How many times: Once per Append call; no loops inside Append.
Each Append adds one node with a fixed number of steps, no matter how big the list is.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 appends x fixed steps = 10 |
| 100 | 100 appends x fixed steps = 100 |
| 1000 | 1000 appends x fixed steps = 1000 |
Pattern observation: The work grows linearly as we add more nodes.
Time Complexity: O(1) per Append operation
This means adding a node takes the same small amount of time no matter how big the list is.
[X] Wrong: "Appending a node takes longer as the list grows because we have to find the end each time."
[OK] Correct: The list keeps track of the tail node, so we add directly without searching, keeping the time constant.
Knowing that appending to a doubly linked list is fast helps you explain efficient data structure choices clearly and confidently.
"What if we did not keep a tail pointer and had to find the end each time? How would the time complexity change?"
Practice
prev pointer in a doubly linked list node?Solution
Step 1: Understand the role of pointers in doubly linked list nodes
Each node in a doubly linked list has two pointers: one to the next node and one to the previous node.Step 2: Identify the function of the
Theprevpointerprevpointer specifically points to the node that comes before the current node in the list.Final Answer:
To point to the previous node in the list -> Option AQuick Check:
prevpointer = previous node [OK]
- Confusing
prevwithnextpointer - Thinking
prevstores data - Assuming
prevmarks list end
Solution
Step 1: Recall Go struct pointer usage for linked nodes
In Go, linked list nodes use pointers to other nodes to link them, sonextandprevmust be pointers toNode.Step 2: Check each option for correct pointer types
type Node struct { data int; next *Node; prev *Node } uses*Nodefor bothnextandprev, which is correct. type Node struct { data int; next Node; prev Node } uses non-pointer types, which is incorrect. type Node struct { data int; next *int; prev *int } uses pointers to int, which is wrong. type Node struct { data int; next *Node; prev int } mixes pointer and int, which is invalid.Final Answer:
type Node struct { data int; next *Node; prev *Node } -> Option CQuick Check:
Pointers to Node = correct struct [OK]
- Using non-pointer types for next or prev
- Using wrong pointer types like *int
- Mixing pointer and non-pointer fields
type Node struct {
data int
next *Node
prev *Node
}
func printList(head *Node) {
for head != nil {
fmt.Print(head.data, " ")
head = head.next
}
fmt.Println()
}
func main() {
var head *Node
head = &Node{data: 1}
second := &Node{data: 2}
third := &Node{data: 3}
head.next = second
second.prev = head
second.next = third
third.prev = second
printList(head)
}Solution
Step 1: Understand the node linking order
The nodes are linked as 1 -> 2 -> 3 with proper next and prev pointers.Step 2: Trace the printList function
Starting from head (node with data 1), the function prints 1, moves to next (2), prints 2, moves to next (3), prints 3, then stops at nil.Final Answer:
1 2 3 -> Option BQuick Check:
Traversal from head prints 1 2 3 [OK]
- Confusing prev and next pointers during traversal
- Printing in reverse order mistakenly
- Not linking nodes correctly
func insertAtBeginning(head *Node, data int) *Node {
newNode := &Node{data: data}
newNode.next = head
head.prev = newNode
return newNode
}Solution
Step 1: Analyze the code for edge cases
The code setshead.prev = newNodewithout checking ifheadis nil, which causes a runtime error if the list is empty.Step 2: Identify the missing nil check
Before settinghead.prev, the code should check ifhead != nilto avoid dereferencing nil pointer.Final Answer:
Missing check if head is nil before setting head.prev -> Option AQuick Check:
Check for nil head before accessing prev [OK]
- Not checking for nil head causing runtime panic
- Confusing prev and next pointers
- Incorrect return type assumptions
next and prev pointers of each node. Which of the following code snippets correctly performs this reversal?Solution
Step 1: Understand the reversal logic for doubly linked list
Reversing means swappingnextandprevpointers for each node, then moving to the original next node (which becomes prev after swap).Step 2: Analyze each code snippet for correctness
func reverse(head *Node) *Node { current := head var temp *Node for current != nil { temp = current.prev current.prev = current.next current.next = temp current = current.prev } if temp != nil { head = temp.prev } return head } correctly swaps pointers and moves current tocurrent.prev(original next). It updates head to the last processed node. func reverse(head *Node) *Node { current := head var temp *Node for current != nil { temp = current.next current.next = current.prev current.prev = temp current = current.prev } if temp != nil { head = temp.prev } return head } swaps pointers but moves current incorrectly and updates head wrongly.Final Answer:
func reverse(head *Node) *Node { current := head var temp *Node for current != nil { temp = current.prev current.prev = current.next current.next = temp current = current.prev } if temp != nil { head = temp.prev } return head } -> Option DQuick Check:
Swap next and prev, move current to original next [OK]
- Not updating head to new first node
- Moving current pointer incorrectly after swap
- Returning wrong node as head
Ready to prove it?
Pass the test and this pattern moves unknown
