1. Introduction: Sorting Taxonomy & Complexity Matrix
Sorting algorithms form the backbone of computer science and are heavily analyzed in technical interviews.
+─────────────────────────────────────────────────────────────────────────────+
| SORTING ALGORITHMS COMPLEXITY MATRIX |
+─────────────────────────────────────────────────────────────────────────────+
Algorithm Best Time Avg Time Worst Time Aux Space Stable?
─────────────────────────────────────────────────────────────────────────────
Quick Sort O(N log N) O(N log N) O(N^2) O(log N) No
Merge Sort O(N log N) O(N log N) O(N log N) O(N) Yes
Heap Sort O(N log N) O(N log N) O(N log N) O(1) No
Insertion Sort O(N) O(N^2) O(N^2) O(1) Yes
Counting Sort O(N + K) O(N + K) O(N + K) O(K) Yes
Radix Sort O(d * (N + b)) O(d * (N + b)) O(d * (N + b)) O(N + b) Yes
Timsort (Java/Py)O(N) O(N log N) O(N log N) O(N) Yes
2. Core Interview Questions & Solutions
Q1. Implement Merge Sort (Divide and Conquer)
Problem Statement: Implement stable Merge Sort in O(N log N) time.
pythondef merge_sort(arr: list[int]) -> list[int]: if len(arr) <= 1: return arr mid = len(arr) // 2 left = merge_sort(arr[:mid]) right = merge_sort(arr[mid:]) return merge(left, right) def merge(left: list[int], right: list[int]) -> list[int]: merged = [] i = j = 0 while i < len(left) and j < len(right): if left[i] <= right[j]: # <= maintains stability! merged.append(left[i]) i += 1 else: merged.append(right[j]) j += 1 merged.extend(left[i:]) merged.extend(right[j:]) return merged
- Time Complexity: O(N log N) (all cases) | Space Complexity: O(N)
Q2. Implement In-Place Quick Sort (Hoare / Lomuto Partitioning)
Problem Statement: Implement Quick Sort with randomized pivot selection to prevent worst-case O(N²) quadratic degradation on sorted inputs.
cpp#include <vector> #include <cstdlib> #include <algorithm> int partition(std::vector<int>& arr, int low, int high) { // Randomized Pivot Selection int randomIdx = low + rand() % (high - low + 1); std::swap(arr[randomIdx], arr[high]); int pivot = arr[high]; int i = low - 1; for (int j = low; j < high; j++) { if (arr[j] <= pivot) { i++; std::swap(arr[i], arr[j]); } } std::swap(arr[i + 1], arr[high]); return i + 1; } void quickSort(std::vector<int>& arr, int low, int high) { if (low < high) { int p = partition(arr, low, high); quickSort(arr, low, p - 1); quickSort(arr, p + 1, high); } }
- Time Complexity: O(N log N) average | Space Complexity: O(log N) stack.
Q3. Non-Comparison Sorting: Counting Sort in O(N + K) Time
Problem Statement: Sort an array of non-negative integers bounded by maximum value K in linear time.
javapublic void countingSort(int[] arr, int maxVal) { int[] count = new int[maxVal + 1]; for (int num : arr) { count[num]++; } int index = 0; for (int i = 0; i <= maxVal; i++) { while (count[i] > 0) { arr[index++] = i; count[i]--; } } }
- Time Complexity: O(N + K) | Space Complexity: O(K)
3. Comparison Cheat Sheet for Interviews
- Why do Java and Python use Timsort for objects?
- Timsort is a hybrid of Merge Sort and Insertion Sort designed to exploit natural runs of already-sorted data in real-world inputs, achieving O(N) best-case time while maintaining strict stability.
- Why is Quick Sort often faster than Merge Sort in practice despite same O(N log N)?
- Quick Sort operates completely in-place with exceptional CPU cache locality and lower constant factors, whereas Merge Sort incurs memory allocation overhead for auxiliary merge buffers.
Practice sorting coding problems on DSAMaster's practice platform.
