Quick Sort
Table of Contents + β
Say you have a big pile of exam papers to sort by marks. Doing it one swap at a time is slow. So you do something smarter. You pick one paper. Then you push every lower-mark paper to its left and every higher-mark paper to its right. Now that one paper is already in its final spot. After that you do the same trick on the left pile and on the right pile.
That is exactly what Quick Sort does. Quick Sort is a sorting method that picks one element, called the pivot, and splits the array around it. The smaller items move to its left and the bigger items move to its right. Then it sorts each side the same way.
π― What Problem Does It Solve?
Sorting by hand means comparing and swapping every pair. That takes a lot of work. The pain is all those repeated passes over the whole array.
Quick Sort cuts the work down. Each time it places the pivot in its correct final position. Then it only has to deal with two smaller pieces. So the big problem keeps breaking into smaller and smaller problems. This goes on until each piece is tiny and already sorted.
This idea of breaking a problem into smaller copies of itself is called divide and conquer. You divide the array, solve the small parts, and the whole thing ends up sorted.
π§© The Pivot and the Partition
The heart of Quick Sort is one move called partition. Partition means rearranging the array around a chosen pivot. Everything smaller sits before it and everything larger sits after it.
Here is the plan in plain words:
- Pick a pivot. We will use the last element. It is simple and works fine for learning.
- Walk through the rest of the array from left to right.
- Keep a marker for the boundary of the βsmaller than pivotβ zone.
- Every time you find an element smaller than the pivot, swap it into that zone and move the marker forward.
- At the end, swap the pivot into the spot right after the zone. Now the pivot is in its final place.
This style of partition uses the last element as the pivot and one marker to grow the smaller zone. It is called the Lomuto partition.
π A Small Trace
Let us partition this array. The pivot is the last element, 4.
Array: [7, 2, 9, 1, 4]Pivot: 4 (the last element)We use a marker i that sits just before the smaller zone. It starts at -1, since there are no smaller items yet. We scan with j from the first element up to the one before the pivot.
Let us follow the swaps step by step:
- Start:
[7, 2, 9, 1, 4], markeri = -1. j = 0, value7. Is7less than4? No. Skip.j = 1, value2. Is2less than4? Yes. Moveito0, then swap positions0and1. The array becomes[2, 7, 9, 1, 4].j = 2, value9. Less than4? No. Skip.j = 3, value1. Less than4? Yes. Moveito1, then swap positions1and3. The array becomes[2, 1, 9, 7, 4].- The scan is done. Now swap the pivot in the last spot with position
i + 1, which is2. The array becomes[2, 1, 4, 7, 9].
Now the pivot 4 is in its final position. Everything to its left is smaller and everything to its right is larger.
Note
After one partition the pivot is locked in place forever. We never touch it again. Quick Sort then repeats the same partition on the left part [2, 1] and the right part [7, 9].
π Steps to Quick Sort an Array
Here is the full method, written as a recipe you can follow.
- If the part you are looking at has one element or none, it is already sorted. Stop.
- Run partition on the current part. This puts the pivot in its final position and gives you the pivot index.
- Apply Quick Sort to the left part, from the start up to just before the pivot.
- Apply Quick Sort to the right part, from just after the pivot to the end.
- When all parts are done, the whole array is sorted.
Steps 3 and 4 call Quick Sort again on smaller pieces. A function that calls itself like this is called recursion.
π» Quick Sort in 5 Languages
Below is Quick Sort with a Lomuto partition helper. The partition function does the rearranging and returns the pivotβs final index. The quickSort function calls itself on the left and right pieces.
# Lomuto partition: pivot is the last elementdef partition(arr, low, high): pivot = arr[high] # chosen pivot i = low - 1 # boundary of the smaller zone
for j in range(low, high): if arr[j] < pivot: # found a smaller element i += 1 arr[i], arr[j] = arr[j], arr[i] # swap into zone
arr[i + 1], arr[high] = arr[high], arr[i + 1] # place pivot return i + 1 # pivot index
# Recursively sort the arraydef quick_sort(arr, low, high): if low < high: p = partition(arr, low, high) quick_sort(arr, low, p - 1) # sort left part quick_sort(arr, p + 1, high) # sort right part
arr = [7, 2, 9, 1, 4]quick_sort(arr, 0, len(arr) - 1)print(arr)The output of the above code will be:
[1, 2, 4, 7, 9]Tip
Partition does the real work. Quick Sort itself is just βpartition, then call yourself on the two halves.β If you understand partition, you understand Quick Sort.
β±οΈ How Fast Is It?
The speed of Quick Sort depends a lot on the pivot you pick. The very same algorithm can be fast or slow. It comes down to luck and to how you choose the pivot.
When the pivot lands near the middle each time, the array splits into two near-equal halves. That gives O(n log n) time, which is fast. This is the average case and the best case.
The bad case is when the pivot is always the smallest or the largest element. Then one side is empty and the other side holds everything. The array shrinks by just one each time, which gives O(n^2) time. This happens, for example, when you use the last-element pivot on an array that is already sorted.
Caution
Pivot choice matters a lot. A common fix is to pick a random element as the pivot, or to use the median of the first, middle, and last elements. This makes the slow O(n^2) case very unlikely on real data.
Quick Sort sorts the array in place, so it does not need a second array. But recursion uses the call stack, and that needs about O(log n) space when the splits are balanced.
One more thing. Quick Sort is not stable. Stable means equal elements keep their original order. Quick Sort can swap equal elements past each other, so their order may change.
Here is the full picture.
| Case | Time Complexity | When It Happens |
|---|---|---|
| Best | O(n log n) | Pivot splits the array into even halves |
| Average | O(n log n) | Typical mixed data |
| Worst | O(n^2) | Pivot is always smallest or largest |
| Space | O(log n) | Recursion call stack, balanced splits |
β οΈ Common Mistakes
A few small slips trip up almost everyone the first time.
- Starting the marker
iatlowinstead oflow - 1. Then the very first smaller element lands in the wrong spot. - Forgetting the final swap that places the pivot. Without it, the pivot never reaches its correct position.
- Recursing with the wrong bounds. The left call goes up to
p - 1and the right call starts atp + 1. If you includepagain, it can loop forever. - Always using the last element as the pivot on sorted data. That triggers the slow O(n^2) case. Pick a random pivot to stay safe.
- Missing the base case
low < high. Without it the recursion never stops.
π§© What Youβve Learned
- β Quick Sort picks a pivot, partitions the array around it, then sorts the two sides.
- β Partition pushes smaller elements left and larger ones right, then locks the pivot in its final place.
- β The Lomuto partition uses the last element as pivot and one marker to grow the smaller zone.
- β Quick Sort runs in O(n log n) on average but can drop to O(n^2) with bad pivots.
- β It sorts in place, uses about O(log n) stack space, and is not stable.
- β Picking the pivot well, with a random pivot or the median of three, keeps the slow case away.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What does the partition step do?
Why: Partition places the pivot in its final spot, with smaller elements before it and larger ones after it.
- 2
In the Lomuto partition shown, which element is chosen as the pivot?
Why: This Lomuto version uses the last element of the current part as the pivot.
- 3
What is the average time complexity of Quick Sort?
Why: On typical data the pivot splits the array fairly evenly, giving O(n log n).
- 4
When does Quick Sort hit its worst case of O(n^2)?
Why: A consistently smallest or largest pivot leaves one side empty, so the array shrinks by only one each time.