Quick Sort as Divide and Conquer in DSA Java - Time & Space Complexity
We want to understand how the time to sort a list using Quick Sort changes as the list grows.
How does Quick Sort's work increase when the input size gets bigger?
Analyze the time complexity of the following code snippet.
public void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pivotIndex = partition(arr, low, high);
quickSort(arr, low, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, high);
}
}
private int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] <= pivot) {
i++;
int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp;
}
}
int temp = arr[i + 1]; arr[i + 1] = arr[high]; arr[high] = temp;
return i + 1;
}
This code sorts an array by picking a pivot, dividing the array around it, and sorting the parts recursively.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The partition loop that compares and swaps elements around the pivot.
- How many times: This loop runs for each recursive call, covering parts of the array until fully sorted.
- Recursion: The array is split into two parts and quickSort is called on each part.
When the array size doubles, the partition work roughly doubles, but the recursive calls split the problem.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 50 to 70 comparisons and swaps |
| 100 | About 700 to 900 comparisons and swaps |
| 1000 | About 10,000 to 13,000 comparisons and swaps |
Pattern observation: The work grows a bit faster than the input size but much slower than the square of input size.
Time Complexity: O(n log n)
This means the time to sort grows a little more than linearly, multiplying by the input size and the number of splits.
[X] Wrong: "Quick Sort always sorts in linear time because it splits the array."
[OK] Correct: Sometimes the splits are uneven, causing more work and slower sorting, especially if the pivot is poor.
Understanding Quick Sort's time complexity helps you explain how divide and conquer algorithms work efficiently in real problems.
"What if we always picked the middle element as pivot instead of the last? How would the time complexity change?"
Practice
Solution
Step 1: Understand divide and conquer in Quick Sort
Quick Sort divides the array into parts based on a pivot, sorts each part separately.Step 2: Recognize the combining step
After sorting parts, the array is combined naturally as parts are sorted in place.Final Answer:
Divide the array into smaller parts, sort them, and combine the results -> Option AQuick Check:
Divide and conquer = split, sort, combine [OK]
- Confusing Quick Sort with Bubble Sort
- Thinking Quick Sort uses extra space like Merge Sort
- Believing Quick Sort only swaps adjacent elements
Solution
Step 1: Identify valid pivot index
Array indices go from 0 to length-1, so middle index is length/2.Step 2: Check each option
A uses length (undefined, invalid), B uses arr.length (out of bounds), C uses middle element (valid), D uses negative index (invalid in Java).Final Answer:
int pivot = arr[arr.length / 2]; -> Option BQuick Check:
Pivot index must be within 0 to length-1 [OK]
- Using arr.length as index (out of bounds)
- Using negative indices (not allowed in Java)
- Always choosing first element without reason
int[] arr = {4, 7, 2, 9, 1} if pivot is chosen as the first element?Solution
Step 1: Choose pivot and partition
Pivot = 4. Elements less than 4 go left, greater go right.Step 2: Rearrange array after partition
Left side: 2,1; pivot:4; right side:7,9. So array becomes [2, 1, 4, 7, 9].Final Answer:
[2, 1, 4, 7, 9] -> Option AQuick Check:
Partition arr around pivot 4 = [2,1,4,7,9] [OK]
- Not moving elements smaller than pivot to left
- Leaving array unchanged after partition
- Placing pivot at wrong position
int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j <= high; j++) {
if (arr[j] <= pivot) {
i++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
return i;
}Solution
Step 1: Analyze loop boundary
The loop runs j from low to high inclusive, but arr[high] is pivot and should not be compared inside loop.Step 2: Correct loop condition
Loop should run j from low to high-1 to avoid swapping pivot with itself.Final Answer:
Loop should run until j < high, not j <= high -> Option CQuick Check:
Partition loop excludes pivot index [OK]
- Including pivot in partition loop
- Starting i at wrong index
- Changing pivot selection unnecessarily
{10, 80, 30, 90, 40, 50, 70}, if Quick Sort always chooses the last element as pivot, what is the sequence of pivot values chosen during the entire sorting process?Solution
Step 1: Identify pivots chosen at each recursive call
First pivot: last element 70. After partition, left subarray ends at 50, right subarray ends at 80.Step 2: Continue choosing last element as pivot in subarrays
Next pivots: 50 (left), then 40, then 30, then 10; right subarray pivots: 80, then 90.Final Answer:
[70, 50, 40, 30, 10, 80, 90] -> Option DQuick Check:
Last element pivot sequence matches [OK]
- Mixing pivot order with sorted array order
- Choosing first element as pivot instead
- Ignoring recursive calls on subarrays
Ready to prove it?
Pass the test and this pattern moves unknown
