Find Middle Element of Linked List in DSA Go - Time & Space Complexity
We want to understand how long it takes to find the middle element in a linked list as the list grows.
How does the time needed change when the list gets bigger?
Analyze the time complexity of the following code snippet.
func findMiddle(head *Node) *Node {
slow := head
fast := head
for fast != nil && fast.Next != nil {
slow = slow.Next
fast = fast.Next.Next
}
return slow
}
This code finds the middle node of a singly linked list using two pointers moving at different speeds.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The loop that moves the fast pointer two steps and slow pointer one step each time.
- How many times: The loop runs about half the number of nodes in the list (n/2 times).
As the list size grows, the number of loop steps grows roughly half as fast.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 5 |
| 100 | 50 |
| 1000 | 500 |
Pattern observation: The operations grow linearly with the size of the list, just about half as many steps.
Time Complexity: O(n)
This means the time to find the middle grows directly in proportion to the list size.
[X] Wrong: "Since the fast pointer jumps two steps, the time complexity is O(1)."
[OK] Correct: Even though fast moves faster, the loop still depends on the list length, so time grows with n.
Understanding this pattern helps you explain efficient traversal techniques and shows you can analyze pointer-based algorithms clearly.
"What if we used only one pointer moving one step at a time and counted nodes first? How would the time complexity change?"
Practice
Solution
Step 1: Understand two-pointer technique
The slow pointer moves one node at a time, while the fast pointer moves two nodes at a time.Step 2: Identify middle when fast pointer reaches end
When the fast pointer reaches the end of the list, the slow pointer will be at the middle node.Final Answer:
Use one slow pointer moving one step and one fast pointer moving two steps until fast reaches the end -> Option AQuick Check:
Two-pointer technique = Use one slow pointer moving one step and one fast pointer moving two steps until fast reaches the end [OK]
- Moving both pointers at the same speed
- Counting nodes first instead of using pointers
- Reversing the list unnecessarily
head?Solution
Step 1: Initialize both pointers at head
Both slow and fast pointers should start at the head node to traverse the list properly.Step 2: Check pointer initialization correctness
slow, fast := head, head correctly sets slow and fast to head, allowing traversal from the start.Final Answer:
slow, fast := head, head -> Option CQuick Check:
Both pointers start at head = slow, fast := head, head [OK]
- Setting fast pointer to nil initially
- Starting pointers at different nodes
- Skipping nodes at initialization
type Node struct {
val int
next *Node
}
func findMiddle(head *Node) int {
slow, fast := head, head
for fast != nil && fast.next != nil {
slow = slow.next
fast = fast.next.next
}
return slow.val
}
// List: 10 -> 20 -> 30 -> 40 -> 50 -> nil
head := &Node{10, &Node{20, &Node{30, &Node{40, &Node{50, nil}}}}}
fmt.Println(findMiddle(head))Solution
Step 1: Trace slow and fast pointers through the list
Initially, slow=10, fast=10. After first loop: slow=20, fast=30. Second loop: slow=30, fast=50. Third loop ends as fast.next is nil.Step 2: Identify slow pointer value at loop end
Slow pointer is at node with value 30, which is the middle element.Final Answer:
30 -> Option AQuick Check:
Middle element = 30 [OK]
- Returning fast pointer value instead of slow
- Stopping loop too early
- Confusing node values
func findMiddle(head *Node) int {
slow, fast := head, head
for fast.next != nil && fast.next.next != nil {
slow = slow.next
fast = fast.next.next
}
return slow.val
}Solution
Step 1: Analyze loop condition safety
The loop checks fast.next and fast.next.next but does not check if fast itself is nil, risking a runtime error if fast is nil.Step 2: Correct loop condition to avoid nil pointer dereference
The condition should first check fast != nil, then fast.next != nil to safely access fast.next.next.Final Answer:
The loop condition should check fast != nil before fast.next -> Option DQuick Check:
Check fast != nil first to avoid errors [OK]
- Ignoring nil checks causing runtime panic
- Moving slow pointer incorrectly
- Returning wrong pointer value
Solution
Step 1: Understand middle element in even-length list
For even nodes, two middle elements exist; the second middle is the one after the first middle.Step 2: Modify fast pointer start to shift middle selection
Starting fast pointer at head.next causes slow pointer to move to the second middle when fast reaches the end.Final Answer:
Initialize fast pointer to head.next instead of head -> Option BQuick Check:
Start fast at head.next for second middle [OK]
- Moving pointers incorrectly
- Returning fast pointer value
- Reversing list unnecessarily
Ready to prove it?
Pass the test and this pattern moves unknown
