Quick Sort Partition Lomuto and Hoare in DSA Rust - Time & Space Complexity
We want to understand how fast the partition steps in Quick Sort run as the input size grows.
This helps us see how the sorting speed changes with bigger lists.
Analyze the time complexity of the following partition functions used in Quick Sort.
fn lomuto_partition(arr: &mut [i32]) -> usize {
let pivot = arr[arr.len() - 1];
let mut i = 0;
for j in 0..arr.len() - 1 {
if arr[j] <= pivot {
arr.swap(i, j);
i += 1;
}
}
arr.swap(i, arr.len() - 1);
i
}
fn hoare_partition(arr: &mut [i32]) -> usize {
let pivot = arr[0];
let mut i = 0;
let mut j = arr.len() - 1;
loop {
while arr[i] < pivot { i += 1; }
while arr[j] > pivot { j -= 1; }
if i >= j { return j; }
arr.swap(i, j);
i += 1;
j -= 1;
}
}
These functions rearrange elements around a pivot to help sort the list.
Both functions scan parts of the array multiple times.
- Primary operation: Looping through array elements to compare with pivot and swap.
- How many times: Each element is checked at least once per partition call.
As the list size grows, the number of comparisons and swaps grows roughly in direct proportion.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 comparisons and swaps |
| 100 | About 100 comparisons and swaps |
| 1000 | About 1000 comparisons and swaps |
Pattern observation: The work grows linearly with the number of elements.
Time Complexity: O(n)
This means the partition step takes time proportional to the list size each time it runs.
[X] Wrong: "Partitioning is a constant time step because it just picks a pivot."
[OK] Correct: Partitioning compares and swaps many elements, so its time grows with the list size.
Understanding partition time helps you explain Quick Sort's efficiency clearly and confidently in interviews.
"What if the pivot was always chosen as the middle element? How would that affect the partition time complexity?"
Practice
Solution
Step 1: Understand Lomuto partition
Lomuto partition uses one pointer to track the boundary of smaller elements and swaps elements accordingly.Step 2: Understand Hoare partition
Hoare partition uses two pointers starting from both ends moving inward to swap elements that are out of place.Final Answer:
Lomuto uses a single pointer, Hoare uses two pointers moving inward -> Option AQuick Check:
Partition pointers = single vs two [OK]
- Confusing which partition uses one or two pointers
- Thinking both partitions use the same pointer strategy
- Assuming Hoare partition sorts the array directly
Solution
Step 1: Identify mutability and types
Lomuto partition modifies the array, so it needs a mutable slice &mut [i32]. Indices are usize in Rust for slices.Step 2: Check return type
Partition returns the pivot index, which is usize to match slice indexing.Final Answer:
fn lomuto_partition(arr: &mut [i32], low: usize, high: usize) -> usize -> Option CQuick Check:
Mutable slice + usize indices = fn lomuto_partition(arr: &mut [i32], low: usize, high: usize) -> usize [OK]
- Using immutable slice &[] when mutation is needed
- Using i32 for indices instead of usize
- Returning wrong type for pivot index
[8, 3, 7, 6, 2] with pivot as last element (2)?Solution
Step 1: Choose pivot and initialize pointers
Pivot is last element 2. Start i at low-1 = -1 (conceptually), j iterates from low to high-1.Step 2: Iterate and swap
Check each element: 8 > 2 no swap, 3 > 2 no swap, 7 > 2 no swap, 6 > 2 no swap. Finally swap pivot with element at i+1 = 0.Final Answer:
[2, 3, 7, 6, 8] with pivot index 0 -> Option BQuick Check:
Pivot 2 placed at index 0 [OK]
- Not swapping pivot to correct place
- Confusing pivot index with array values
- Assuming array is fully sorted after partition
fn hoare_partition(arr: &mut [i32], low: usize, high: usize) -> usize {
let pivot = arr[low];
let mut i = low - 1;
let mut j = high + 1;
loop {
loop { i += 1; if arr[i] >= pivot { break; } }
loop { j -= 1; if arr[j] <= pivot { break; } }
if i >= j { return j; }
arr.swap(i, j);
}
}Solution
Step 1: Analyze pointer initialization
i is initialized as low - 1 and j as high + 1, but usize cannot be negative, so low - 1 causes underflow.Step 2: Understand Rust usize behavior
usize is unsigned, subtracting 1 from 0 wraps to max usize value, causing runtime errors or wrong behavior.Final Answer:
Using usize for i and j causes underflow when subtracting 1 -> Option DQuick Check:
Unsigned underflow = bug [OK]
- Ignoring underflow in unsigned integers
- Assuming pivot must be last element
- Swapping before checking pointers cross
[10, 80, 30, 90, 40, 50, 70], which partition scheme (Lomuto or Hoare) will perform fewer swaps during the first partition step with pivot as last element (70)?Solution
Step 1: Understand swap behavior of Lomuto
Lomuto swaps every time it finds an element smaller than pivot, plus one final swap for pivot placement.Step 2: Understand swap behavior of Hoare
Hoare swaps only when two pointers find elements on wrong sides, often fewer swaps than Lomuto.Step 3: Apply to given array
For this array, Lomuto will swap multiple times moving smaller elements forward; Hoare will swap fewer pairs crossing pointers.Final Answer:
Hoare partition performs fewer swaps -> Option AQuick Check:
Hoare swaps fewer times [OK]
- Assuming Lomuto always swaps less
- Ignoring pointer crossing logic in Hoare
- Thinking swap count equals number of elements
Ready to prove it?
Pass the test and this pattern moves unknown
