Merge, Quick & Heap Sort
Divide & conquer and tree‑based sorting: understand recursive splitting, partitioning, and heap‑based selection with pseudocode, analysis, and comparative insights.
📌 Advanced Sorting: Moving beyond O(n²) iterative sorts, these three algorithms achieve O(n log n) average time complexity. Merge Sort uses the divide‑and‑conquer paradigm, Quick Sort is a fast in‑place partition‑based sort, and Heap Sort leverages a binary heap data structure.
🎯 What you'll learn: Pseudocode, time/space complexity, stability, in‑place behaviour, pivot strategies (Quick Sort), heapify process (Heap Sort), and a comparative matrix to choose the right algorithm.
Merge Sort
Quick Sort
Heap Sort
1 Merge Sort StableNot In‑place
How it works: Recursively splits the array into two halves until each subarray has one element, then merges them back together in sorted order. The merging step is the core: two sorted arrays are combined into one.
Complexity Analysis
| Case | Time Complexity | Space Complexity |
|---|---|---|
| Best | O(n log n) | O(n) (auxiliary) |
| Average | O(n log n) | O(n) |
| Worst | O(n log n) | O(n) |
Stability: ✅ Stable – merging preserves order of equal elements.
In‑place: ❌ Requires extra space proportional to the input.
2 Quick Sort UnstableIn‑place
How it works: Chooses a pivot element and partitions the array so that elements less than pivot come before it and elements greater come after. Recursively sorts the subarrays. The partition step is the heart of the algorithm.
Complexity Analysis
| Case | Time Complexity | Space Complexity |
|---|---|---|
| Best (balanced partitions) | O(n log n) | O(log n) (stack) |
| Average | O(n log n) | O(log n) |
| Worst (sorted / reverse) | O(n²) | O(n) (stack) |
Stability: ❌ Unstable – partitioning may swap equal elements.
In‑place: ✅ Yes, only recursive stack space used.
3 Heap Sort UnstableIn‑place
How it works: First builds a max‑heap from the input array, then repeatedly extracts the maximum element (root) and places it at the end of the array, reducing the heap size by one each time. Relies on the heapify procedure.
Complexity Analysis
| Case | Time Complexity | Space Complexity |
|---|---|---|
| Best | O(n log n) | O(1) |
| Average | O(n log n) | O(1) |
| Worst | O(n log n) | O(1) |
Stability: ❌ Unstable – heap operations may change relative order.
In‑place: ✅ Yes, no extra array needed.
Comparative Analysis
All three achieve O(n log n) average time, but differ in space usage, stability, and worst‑case behaviour. Here’s the definitive comparison:
| Property | Merge Sort | Quick Sort | Heap Sort |
|---|---|---|---|
| Average Time | O(n log n) | O(n log n) | O(n log n) |
| Worst Time | O(n log n) | O(n²) | O(n log n) |
| Space | O(n) | O(log n) (stack) | O(1) |
| Stable | ✅ | ❌ | ❌ |
| In‑place | ❌ | ✅ | ✅ |
| Adaptive | ❌ | ✅ (with proper pivot) | ❌ |
| Preferred for | External sorting, linked lists | General purpose, small overhead | Memory‑constrained, worst‑case guarantee |
DSA Questions & Drills
Apply your understanding with these classic problems.
🔰 Beginner Level
1. Implement Merge Sort for an array of strings
Problem: Write a function that sorts a list of words alphabetically using Merge Sort.
def merge_sort_strings(arr):
if len(arr) > 1:
mid = len(arr)//2
L = arr[:mid]
R = arr[mid:]
merge_sort_strings(L)
merge_sort_strings(R)
i=j=k=0
while i < len(L) and j < len(R):
if L[i] <= R[j]:
arr[k] = L[i]
i+=1
else:
arr[k] = R[j]
j+=1
k+=1
while i < len(L): arr[k]=L[i]; i+=1; k+=1
while j < len(R): arr[k]=R[j]; j+=1; k+=1
return arr2. Quick Sort with random pivot
Problem: Modify Quick Sort to use a randomly chosen pivot to avoid worst‑case on sorted input.
import random
def quick_sort_random(arr, low, high):
if low < high:
pi = partition_random(arr, low, high)
quick_sort_random(arr, low, pi-1)
quick_sort_random(arr, pi+1, high)
def partition_random(arr, low, high):
pivot_idx = random.randint(low, high)
arr[pivot_idx], arr[high] = arr[high], arr[pivot_idx]
pivot = arr[high]
i = low-1
for j in range(low, high):
if arr[j] <= pivot:
i+=1
arr[i], arr[j] = arr[j], arr[i]
arr[i+1], arr[high] = arr[high], arr[i+1]
return i+13. Build a max‑heap from an array and extract top K
Problem: Given an unsorted list, use heap operations to find the 3 largest elements.
import heapq
def top_3(arr):
heapq.heapify(arr) # min-heap
return heapq.nlargest(3, arr)
# Or manually with max-heap using negative values⚡ Intermediate Level
4. Count inversions using Merge Sort
Problem: Modify Merge Sort to count the number of inversions (i
def merge_count(arr, temp, left, mid, right):
i = left; j = mid+1; k = left; inv = 0
while i <= mid and j <= right:
if arr[i] <= arr[j]:
temp[k] = arr[i]; i+=1
else:
temp[k] = arr[j]; inv += (mid-i+1); j+=1
k+=1
while i <= mid: temp[k] = arr[i]; i+=1; k+=1
while j <= right: temp[k] = arr[j]; j+=1; k+=1
for x in range(left, right+1): arr[x] = temp[x]
return inv
def merge_sort_inv(arr, temp, left, right):
inv = 0
if left < right:
mid = (left+right)//2
inv += merge_sort_inv(arr, temp, left, mid)
inv += merge_sort_inv(arr, temp, mid+1, right)
inv += merge_count(arr, temp, left, mid, right)
return inv5. Kth largest element using Quick Select (derived from Quick Sort)
Problem: Find the Kth largest element in average O(n) using partitioning.
def kth_largest(arr, k):
def quick_select(low, high):
pivot = arr[high]
i = low
for j in range(low, high):
if arr[j] >= pivot: # for largest
arr[i], arr[j] = arr[j], arr[i]
i+=1
arr[i], arr[high] = arr[high], arr[i]
if i == k-1: return arr[i]
elif i > k-1: return quick_select(low, i-1)
else: return quick_select(i+1, high)
return quick_select(0, len(arr)-1)Unsolved Practice
Test yourself: implement these without peeking at the solution.
1. Merge Sort on a linked list (without extra space)
Problem: Sort a singly linked list using merge sort. You should split the list recursively and merge without auxiliary arrays. (Hint: find middle, break link, merge sorted halves.)
2. Iterative Quick Sort (without recursion)
Problem: Implement Quick Sort using an explicit stack to simulate recursion. Push subarray boundaries onto the stack.
3. Heap Sort in descending order (min‑heap version)
Problem: Modify heap sort to sort an array in descending order by building a min‑heap and extracting minimum each time.