Find Middle Element of Linked List in DSA Python - 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.
class Node:
def __init__(self, data):
self.data = data
self.next = None
def find_middle(head):
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow.data
This code finds the middle element of a linked list by moving two pointers at different speeds.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The while loop that moves the fast and slow pointers through the list.
- How many times: The loop runs about half the number of nodes in the list.
As the list size grows, the number of steps to find the middle grows roughly in half.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 5 steps |
| 100 | 50 steps |
| 1000 | 500 steps |
Pattern observation: The steps increase linearly with the size of the list, but only about half as many steps are needed.
Time Complexity: O(n)
This means the time to find the middle grows directly with the number of elements in the list.
[X] Wrong: "Since the fast pointer jumps two steps, the time complexity is O(n/2), which is faster than O(n)."
[OK] Correct: We ignore constants in Big-O, so O(n/2) is the same as O(n). The time still grows linearly with input size.
Understanding this pattern helps you solve problems efficiently by using two pointers moving at different speeds, a common technique in coding challenges.
"What if we used only one pointer to traverse the list twice instead of two pointers at once? How would the time complexity change?"
Practice
Solution
Step 1: Understand two-pointer movement
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 ends
When the fast pointer reaches the end (null), 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 DQuick Check:
Two-pointer technique = slow one step, fast two steps [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
To start traversal, both slow and fast pointers should begin at the head node.Step 2: Confirm correct initialization
Setting slow = head and fast = head allows fast to move two steps and slow one step safely.Final Answer:
slow = head\nfast = head -> Option AQuick Check:
Both pointers start at head [OK]
- Starting slow or fast at null
- Starting fast pointer too far ahead
- Skipping initialization of pointers
class Node:
def __init__(self, val):
self.val = val
self.next = None
def find_middle(head):
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow.val
# Creating linked list: 10 -> 20 -> 30 -> 40 -> 50
head = Node(10)
head.next = Node(20)
head.next.next = Node(30)
head.next.next.next = Node(40)
head.next.next.next.next = Node(50)
print(find_middle(head))Solution
Step 1: Trace pointer movements
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 null.Step 2: Identify slow pointer value
Slow pointer is at node with value 30 when loop ends, so 30 is middle.Final Answer:
30 -> Option AQuick Check:
Middle element = 30 [OK]
- Returning fast pointer value instead of slow
- Off-by-one errors in loop condition
- Confusing node values during tracing
def find_middle(head):
slow = head
fast = head
while fast.next and fast.next.next:
slow = slow.next
fast = fast.next.next
return slow.valSolution
Step 1: Analyze loop condition
The condition 'while fast.next and fast.next.next' can cause an error if fast is null initially.Step 2: Correct loop condition
Changing it to 'while fast and fast.next' ensures fast is not null before accessing fast.next.Final Answer:
Change loop condition to 'while fast and fast.next' to avoid NoneType errors -> Option BQuick Check:
Safe loop condition = while fast and fast.next [OK]
- Accessing fast.next without checking fast
- Returning wrong pointer's value
- Incorrect pointer initialization
For example, in the list
1 -> 2 -> 3 -> 4 -> null, what will be the middle element returned by the standard two-pointer approach?Solution
Step 1: Trace pointers on even-length list
Start: slow=1, fast=1. Loop 1: slow=2, fast=3. Loop 2: fast tries to move two steps but reaches end, loop stops.Step 2: Identify slow pointer position
Slow pointer is at node with value 3 when loop ends, which is the second middle node.Final Answer:
Node with value 3 (the second middle) -> Option CQuick Check:
Two-pointer returns second middle in even list [OK]
- Assuming it returns first middle node
- Confusing head or last node as middle
- Not tracing pointer steps carefully
Ready to prove it?
Pass the test and this pattern moves unknown
