Quicksort is an efficient recursive sorting algorithm that uses divide and conquer paradigm to sort faster. It can be implemented in-place, so it doesn’t require additional memory.
In practice, quicksort outperforms other sorting algorithms like part04-algorithmic-toolbox.asc. And, of course, It also outperforms simple sorting algorithms like part04-algorithmic-toolbox.asc, part04-algorithmic-toolbox.asc and part04-algorithmic-toolbox.asc.
Quicksort picks a "pivot" element randomly and moves all the smaller parts than the pivot to the left and the ones that are bigger to the right. It does this recursively until all the array is sorted.
Quicksort implementation uses the divide-and-conquer in the following way:
-
Pick a "pivot" element (at random).
-
Move everything lower than the pivot to the left, and everything more significant than the pivot to the right.
-
Recursively repeat step #1 and #2 in the sub-arrays on the left and on the right WITHOUT including the pivot.
Let’s convert these words into code!
link:../../../src/algorithms/sorting/quick-sort.js[role=include]
-
Partition: picks a pivot and find the index where the pivot will be when the array is sorted.
-
Do the partition of the sub-array at the left of the pivot.
-
Do the partition of the sub-array at the right of the pivot.
-
Only do the partition when there’s something to divide.
The partition
function does the real heavy lifting. 🏋️♀️
link:../../../src/algorithms/sorting/quick-sort.js[role=include]
-
Take the leftmost element as the pivot.
-
pivotFinalIndex
is the placeholder for the final position where the pivot will be placed when the array is sorted. -
Check all values other than the pivot to see if any value is smaller than the pivot.
-
If the
current
element’s value is less than the pivot, then incrementpivotFinalIndex
to make room on the left side. -
We also swap the smaller element to the left side since it’s smaller than the pivot.
-
Finally, we move the pivot to its final position. Everything to the left is smaller than the pivot, and everything to the right is bigger.
What would happen if use Quicksort for an array in reverse order?
E.g. [10, 7, 5, 4, 2, 1]
, if we always choose the first element as the pivot, we would have to swap everything to the left of 10
.
So in the first partition, we would have [7, 5, 4, 2, 1, 10]
.
Then, we take 7
would be the next pivot, and we have to swap everything to the left.
Descending arrays are the worst-case for this Quicksort since it will perform O(n2) work.
If we do it by the middle (or even better at random) instead of partitioning by the first element, we would have better performance. That’s why we usually shuffle the array before doing Quicksort to avoid edge cases.
link:../../../src/algorithms/sorting/quick-sort.js[role=include]
-
Convert to array (or clone array). If you want to modify the input, directly remove this line.
-
Shuffle array to avoid edge cases (desc order arrays)
And you can see the implementation of shuffle
below:
link:../../../src/algorithms/sorting/sorting-common.js[role=include]
With the optimization, Quicksort has an O(n log n) running time. Similar to the merge sort, we divide the array into halves each time. For more details about how to extract the runtime, go to part01-algorithms-analysis.asc.
-
[Stable]: ❌ No
-
[In-place]: ✅ Yes
-
[Adaptive]: ️❌ No, mostly sorted array takes the same time O(n log n).
-
[Online]: ️❌ No, the pivot element can be choose at random.
-
Recursive: Yes
-
Time Complexity: ✅ part01-algorithms-analysis.asc O(n log n)
-
Space Complexity: ✅ part01-algorithms-analysis.asc O(log n), because of recursion.