Quick Sort Algorithm in DSA Go - Time & Space Complexity
We want to understand how the time to sort a list using Quick Sort changes as the list grows.
How does the number of steps grow when the list gets bigger?
Analyze the time complexity of the following code snippet.
func quickSort(arr []int) []int {
if len(arr) <= 1 {
return arr
}
pivot := arr[len(arr)/2]
left := []int{}
right := []int{}
middle := []int{}
for _, v := range arr {
if v < pivot {
left = append(left, v)
} else if v > pivot {
right = append(right, v)
} else {
middle = append(middle, v)
}
}
left = quickSort(left)
right = quickSort(right)
return append(append(left, middle...), right...)
}
This code sorts an array by picking a pivot, splitting the array into parts, and sorting each part recursively.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Looping through the array to split it around the pivot.
- How many times: This splitting happens at each recursive call, dividing the array into smaller parts.
Each time we split, we do work proportional to the current size, and then repeat on smaller parts.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 30 to 40 operations |
| 100 | About 700 to 800 operations |
| 1000 | About 10,000 to 15,000 operations |
Pattern observation: The operations grow a bit faster than the input size but not as fast as the square of the input.
Time Complexity: O(n log n)
This means the time to sort grows a little more than linearly, multiplying the input size by its logarithm.
[X] Wrong: "Quick Sort always takes the same time regardless of input order."
[OK] Correct: If the pivot choices are poor, Quick Sort can take much longer, up to O(n^2) in the worst case.
Understanding Quick Sort's time complexity helps you explain why it is fast on average and what cases slow it down, showing your grasp of sorting algorithms.
"What if we always picked the first element as pivot instead of the middle? How would the time complexity change?"
Practice
Quick Sort algorithm?Solution
Step 1: Understand Quick Sort's approach
Quick Sort picks a pivot element and divides the list into parts smaller and larger than the pivot.Step 2: Recursive sorting of parts
It then sorts these parts by calling itself on each part, making the whole list sorted.Final Answer:
Divide the list into smaller parts using a pivot and sort them recursively -> Option DQuick Check:
Quick Sort = Divide and conquer with pivot [OK]
- Confusing Quick Sort with Bubble Sort or Merge Sort
- Thinking Quick Sort compares all pairs directly
- Believing Quick Sort merges sorted lists
Solution
Step 1: Check Go function syntax
In Go, functions are declared withfunc, parameter types after names, and return types after parameters.Step 2: Match correct parameter and return types
arr []intmeans a slice of integers, and[]intas return type means returning a slice of integers.Final Answer:
func quickSort(arr []int) []int -> Option AQuick Check:
Go func syntax = func name(params) returns [OK]
- Using array syntax like int[] which is not Go style
- Writing 'function' instead of 'func'
- Placing types before parameter names
[3, 6, 2, 7, 5]?
func main() {
arr := []int{3, 6, 2, 7, 5}
quickSort(arr, 0, len(arr)-1)
fmt.Println(arr)
}
func quickSort(arr []int, low, high int) {
if low < high {
p := partition(arr, low, high)
quickSort(arr, low, p-1)
quickSort(arr, p+1, high)
}
}
func partition(arr []int, low, high int) int {
pivot := arr[high]
i := low - 1
for j := low; j < high; j++ {
if arr[j] < pivot {
i++
arr[i], arr[j] = arr[j], arr[i]
}
}
arr[i+1], arr[high] = arr[high], arr[i+1]
return i + 1
}Solution
Step 1: Understand partition and recursive calls
The partition function picks the last element as pivot and rearranges elements smaller than pivot to the left. Recursive calls sort left and right parts.Step 2: Trace sorting of [3,6,2,7,5]
Pivot 5 moves elements less than 5 left: [3,2,5,7,6]. Then recursively sort [3,2] and [7,6]. Final sorted array is [2 3 5 6 7].Final Answer:
[2 3 5 6 7] -> Option CQuick Check:
Quick Sort output = sorted array ascending [OK]
- Confusing pivot placement causing wrong order
- Not sorting both left and right parts recursively
- Mixing up indices in partition function
func partition(arr []int, low, high int) int {
pivot := arr[low]
i := low
for j := low + 1; j <= high; j++ {
if arr[j] < pivot {
i++
arr[i], arr[j] = arr[j], arr[i]
}
}
arr[i], arr[low] = arr[low], arr[i]
return i
}Solution
Step 1: Check pivot choice in partition
Standard Quick Sort uses last element as pivot for partitioning, but here pivot is first element which changes logic.Step 2: Understand impact of pivot choice
Using arr[low] as pivot requires different partition logic; current swaps and indices assume pivot at end, causing incorrect partition.Final Answer:
Pivot should be arr[high], not arr[low] -> Option AQuick Check:
Pivot position must match partition logic [OK]
- Changing pivot without adjusting partition logic
- Incorrect swap indices causing wrong array order
- Returning wrong partition index
Solution
Step 1: Understand worst-case in Quick Sort
Worst-case happens when pivot divides list unevenly, e.g., always smallest or largest element.Step 2: Use random pivot to avoid worst-case
Choosing a random pivot reduces chance of always bad splits, improving average performance.Final Answer:
Pick a random element as pivot each time -> Option BQuick Check:
Random pivot reduces worst-case risk [OK]
- Always picking first element causes worst-case on sorted input
- Sorting before Quick Sort wastes effort
- Using Bubble Sort is inefficient for large lists
Ready to prove it?
Pass the test and this pattern moves unknown
