Quick Sort — Interview Preparation
Junior Questions
| # | Question | Answer Focus |
| 1 | What is Quick Sort? | Pick pivot, partition into ≤/≥ halves, recurse. No merge step. |
| 2 | Time complexity? | Best/Avg O(n log n), Worst O(n²). |
| 3 | Space? | O(log n) avg, O(n) worst (recursion stack). |
| 4 | Stable? | No (typical implementations). |
| 5 | In-place? | Yes (just stack overhead). |
| 6 | What's a pivot? | Element used to split the array; ends up at its final sorted position. |
| 7 | Difference from Merge Sort? | No merge step; partition does the sorting work. In-place vs O(n) extra space. |
| 8 | Worst case input? | Already sorted with first/last as pivot — O(n²). |
| 9 | How to avoid worst case? | Random or median-of-three pivot. |
| 10 | Implement Lomuto partition. | See junior.md. |
Middle Questions
| # | Question | Answer Focus |
| 1 | Lomuto vs Hoare partition. | Lomuto: simple, more swaps. Hoare: trickier, ~3× fewer swaps. |
| 2 | What is 3-way partition? | Split into <, ==, > pivot. Handles duplicates in O(n). |
| 3 | Why median-of-three? | Avoids worst case on sorted/reverse inputs. |
| 4 | What is Introsort? | Quick Sort with Heap Sort fallback when depth > 2 log n. C++ STL uses it. |
| 5 | What is Pdqsort? | Pattern-Defeating Quick Sort. Median-of-three + pattern detection + introsort fallback. |
| 6 | What is Dual-Pivot Quicksort? | Two pivots → 3 partitions. Java uses it for primitives. ~5% faster. |
| 7 | Average comparisons? | ~1.39 n log n — about 39% above information-theoretic minimum. |
| 8 | How does tail-recursion optimization help? | Recurse on smaller, iterate on larger → stack bounded to O(log n). |
| 9 | Why doesn't Quick Sort work well on linked lists? | Needs random access for partition; linked lists are sequential. Use Merge Sort. |
| 10 | When to choose Quick Sort over Merge Sort? | In-memory random data, no stability needed, memory-constrained. |
Senior Questions
| # | Question | Answer Focus |
| 1 | Algorithmic complexity attack on Quick Sort? | Adversarial input causes O(n²); defend with random/Pdqsort/input limits. |
| 2 | Implement Quickselect for k-th smallest. | Partition; if pivot index == k, done; else recurse on appropriate side. O(n) avg. |
| 3 | Why does Java use Quick for primitives but Merge for objects? | Primitives don't need stability + cache locality wins; objects often need stability. |
| 4 | When would parallel Quick Sort beat parallel Merge Sort? | Rarely — Merge is easier to parallelize. Quick wins on shared memory. |
| 5 | How to make Quick Sort stable? | Auxiliary index array, sort by (value, index). Defeats in-place benefit. |
| 6 | Compare Pdqsort vs Dual-Pivot vs Introsort. | All Quick Sort variants with different fallback strategies. Pdqsort wins on random; Dual-Pivot ~5% faster on dense ints; Introsort is the C++ choice. |
| 7 | What's the cache behavior of Quick Sort? | O((n/B) log(n/M)) — optimal; partition data stays hot in cache. |
| 8 | When is Quick Sort the wrong choice? | External sort, linked list, real-time SLA, need stability. |
Professional Questions
| # | Question | Answer Focus |
| 1 | Prove Quick Sort correctness via partition invariants. | At each step: A[p..i] ≤ pivot, A[i+1..j-1] > pivot. Induction. |
| 2 | Derive average comparison count. | E[X_{ij}] = 2/(j-i+1); sum gives 1.39 n log n. |
| 3 | Why does randomization give expected O(n log n)? | Each split balanced w.p. ≥ 1/2; depth = O(log n) w.h.p. |
| 4 | Prove Quickselect is O(n) expected. | T(n) = T(3n/4) + O(n) → O(n) by Master Theorem. |
| 5 | What is the Median-of-Medians algorithm? | Deterministic O(n) selection. Recurrence T(n) = T(n/5) + T(7n/10) + O(n). |
| 6 | Cache complexity of Quick Sort? | O((n/B) log(n/M)) — matches lower bound. |
| 7 | Parallel span complexity? | O(log² n) with parallel partition; O(n) sequential partition. |
Coding Challenge
Challenge 1: Implement Quick Sort with Random Pivot
import random
def quick_sort(arr):
_sort(arr, 0, len(arr) - 1)
def _sort(arr, lo, hi):
if lo < hi:
# random pivot
rnd = random.randint(lo, hi)
arr[rnd], arr[hi] = arr[hi], arr[rnd]
p = _partition(arr, lo, hi)
_sort(arr, lo, p - 1)
_sort(arr, p + 1, hi)
def _partition(arr, lo, hi):
pivot = arr[hi]
i = lo - 1
for j in range(lo, hi):
if arr[j] <= pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i+1], arr[hi] = arr[hi], arr[i+1]
return i + 1
Challenge 2: Implement 3-Way Partition
def quick_sort_3way(arr, lo=0, hi=None):
if hi is None: hi = len(arr) - 1
if lo >= hi: return
pivot = arr[lo]; lt, gt, i = lo, hi, lo + 1
while i <= gt:
if arr[i] < pivot:
arr[lt], arr[i] = arr[i], arr[lt]; lt += 1; i += 1
elif arr[i] > pivot:
arr[i], arr[gt] = arr[gt], arr[i]; gt -= 1
else:
i += 1
quick_sort_3way(arr, lo, lt - 1)
quick_sort_3way(arr, gt + 1, hi)
Challenge 3: Quickselect
def quickselect(arr, k):
"""k-th smallest, 0-indexed. O(n) avg."""
lo, hi = 0, len(arr) - 1
while lo < hi:
rnd = random.randint(lo, hi)
arr[rnd], arr[hi] = arr[hi], arr[rnd]
p = _partition(arr, lo, hi)
if p == k: return arr[p]
if p < k: lo = p + 1
else: hi = p - 1
return arr[lo]
Challenge 4: Top-K via Quickselect
def top_k(arr, k):
if k >= len(arr): return list(arr)
a = list(arr)
quickselect(a, k - 1)
return a[:k]
Challenge 5: Iterative Quick Sort (Stack-Based)
def quick_sort_iter(arr):
stack = [(0, len(arr) - 1)]
while stack:
lo, hi = stack.pop()
if lo < hi:
p = _partition(arr, lo, hi)
stack.append((lo, p - 1))
stack.append((p + 1, hi))
Pitfalls
| Pitfall | Fix |
| First/last as pivot on sorted | Use random or median-of-three |
| Not handling duplicates | Use 3-way partition |
| Stack overflow from bad pivots | Tail-recursion optimization |
| Saying "Quick Sort is stable" | False — typical impl is unstable |
| Off-by-one in Hoare partition | Use do-while pattern |
One-Liner
Quick Sort: Pick pivot, partition into ≤/≥ halves, recurse. O(n log n) avg, O(n²) worst, in-place, NOT stable, cache-friendly. Modern variants (Pdqsort, Dual-Pivot, Introsort) eliminate worst case. The fastest in-practice in-memory sort.