Quick Sort

Pseudocode


Analysis
• The performance of Quick Sort is highly dependent on how evenly the partitioning is done.
• Partitioning efficiency depends directly on the pivot choice.
Best Case
• Ideal pivot choice: Always choose the median element.
• This evenly divides the list into two equal parts.
• Resulting in two recursive calls to Quick Sort on lists roughly half the size.
• It requires a linear number of comparisons to partition the elements.
• The recurrence relation is: $$C(n) = 2C\left(\frac{n}{2}\right) + n$$
• By the Master Theorem, the time complexity is $O(n \log n)$.
Worst Case
• Worst pivot choice: An extremal element (smallest or largest).
• Divides the list into an empty list and a list of size $(n - 1)$.
• Only one recursive call is necessary, but it is on a list of size $(n - 1)$.
• The recurrence relation is: $$C(n) = C(n - 1) + n$$
• Solving this by back substitution yields $O(n^2)$.
Average Case
• Even if the list is split by a constant proportion (e.g., 10% and 90%), the time complexity is still $O(n \log n)$.
• A careful average case analysis also yields $$C(n) \approx 1.38n \log n$$
• This remains $O(n \log n)$.
Pivot Choice: Strategies to Avoid the Worst Case
Pivot choice greatly affects performance; several common strategies include:
• Median-of-Three: Select the pivot as the median of three elements (typically the first, middle, and last elements).
– Guarantees that the pivot choice will never be the absolute worst case.
– Does not guarantee $\Theta(n \log n)$ in all edge cases, but is efficient in practice.
• Random Element: Randomly select an index for the pivot element.
– Guarantees an expected average running time of $\Theta(n \log n)$.
– Requires extra work for random number generation.
• Linear Time Median Finding.
– An algorithm exists that runs in $\Theta(n)$ time to find the true median of $n$ objects.
– Guarantees $\Theta(n \log n)$ in all cases (best, average, and worst).
– However, it is complicated, recursive, and has significant constant overhead.
Code Samples
1 | private static void quickSort(int a[], int left, int right) { |
1 | public static int partition(int a[], int left, int right) |
1 | void swap(int *a, int *b) |
A good tutorial with a non-recursive C implementation can be found here: http://alienryderflex.com/quicksort/