Find Middle Element of Linked List in DSA Typescript - Time & Space Complexity
We want to know 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.
function findMiddle(head: ListNode | null): ListNode | null {
let slow = head;
let fast = head;
while (fast !== null && fast.next !== null) {
slow = slow!.next;
fast = fast.next.next;
}
return slow;
}
This code uses two pointers moving at different speeds to find the middle node of a linked list.
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 length of the list, since fast moves two steps each time.
As the list size grows, the number of steps grows roughly in half, but this still grows directly with the list size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 5 steps |
| 100 | About 50 steps |
| 1000 | About 500 steps |
Pattern observation: The steps increase linearly as the list gets longer.
Time Complexity: O(n)
This means the time to find the middle grows directly with the size of the list.
[X] Wrong: "Since fast pointer moves two steps, the time complexity is O(n/2), which is constant time O(1)."
[OK] Correct: Even though fast moves faster, the number of steps still grows as the list grows, so it is linear time, not constant.
Understanding this method shows you can use clever pointer techniques to solve problems efficiently, a skill valued in many coding challenges.
"What if we used only one pointer and counted nodes first, then moved to the middle? How would the time complexity change?"
Practice
Solution
Step 1: Understand the two-pointer technique
One pointer moves one step at a time (slow), the other moves two steps at a time (fast).Step 2: Identify the middle when fast pointer reaches the end
When the fast pointer reaches the end of the list, the slow pointer will be at the middle node.Final Answer:
Use two pointers, one moving twice as fast as the other; when the fast pointer reaches the end, the slow pointer is at the middle. -> Option AQuick Check:
Two-pointer technique = slow at middle when fast ends [OK]
- Trying to count nodes first wastes extra time
- Reversing list is unnecessary and inefficient
- Using extra space like stack is not optimal
Solution
Step 1: Check the type of the next pointer
It should be either another Node or null if it is the last node.Step 2: Verify constructor initialization
The constructor sets value and initializes next to null correctly.Final Answer:
class Node { value: number; next: Node | null; constructor(value: number) { this.value = value; this.next = null; } } -> Option DQuick Check:
Next pointer type includes null and initialized to null [OK]
- Not allowing next to be null causes type errors
- Initializing next to undefined instead of null
- Using any type loses type safety
class Node {
value: number;
next: Node | null;
constructor(value: number) {
this.value = value;
this.next = null;
}
}
function findMiddle(head: Node | null): number | null {
let slow = head;
let fast = head;
while (fast !== null && fast.next !== null) {
slow = slow!.next;
fast = fast.next.next;
}
return slow ? slow.value : null;
}
// Create linked list: 10 -> 20 -> 30 -> 40 -> 50
const head = new Node(10);
head.next = new Node(20);
head.next.next = new Node(30);
head.next.next.next = new Node(40);
head.next.next.next.next = new Node(50);
console.log(findMiddle(head));Solution
Step 1: Trace slow and fast pointers through the list
Initially both at 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 at loop end
Slow pointer is at node with value 30, which is the middle.Final Answer:
30 -> Option BQuick Check:
Slow pointer stops at middle node value 30 [OK]
- Returning fast pointer value instead of slow
- Not checking fast.next in while condition
- Off-by-one errors in pointer movement
function findMiddle(head: Node | null): number | null {
let slow = head;
let fast = head;
while (fast !== null && fast.next !== null) {
slow = slow.next;
fast = fast.next.next;
}
return slow.value;
}Solution
Step 1: Analyze pointer null safety before accessing value
Slow pointer can be null if the list is empty, so accessing slow.value without check can cause runtime error.Step 2: Identify missing null check before return
The function should check if slow is not null before returning slow.value to avoid errors.Final Answer:
The function does not check if slow is null before accessing slow.value. -> Option CQuick Check:
Always check pointer is not null before accessing properties [OK]
- Ignoring null checks causes runtime errors
- Changing fast pointer movement breaks logic
- Returning fast.value misses middle element
Solution
Step 1: Understand difference between first and second middle in even-length list
For even nodes, two middle nodes exist; the second middle is the one after the first middle.Step 2: Modify fast pointer start to head.next
Starting fast pointer at head.next shifts slow pointer to first middle when fast reaches end.Final Answer:
Initialize fast pointer to head.next instead of head and then proceed normally. -> Option AQuick Check:
Fast starts at second node to get first middle [OK]
- Swapping slow and fast pointer steps breaks logic
- Using stack wastes extra space
- Counting nodes twice is inefficient
Ready to prove it?
Pass the test and this pattern moves unknown
