Python Program for Merge Sort with Example and Explanation
merge_sort function that splits the list into halves recursively and merges sorted halves using a merge helper function, like def merge_sort(arr): if len(arr) <= 1: return arr; mid = len(arr)//2; left = merge_sort(arr[:mid]); right = merge_sort(arr[mid:]); return merge(left, right).Examples
How to Think About It
Algorithm
Code
def merge(left, right): result = [] i = j = 0 while i < len(left) and j < len(right): if left[i] < right[j]: result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 result.extend(left[i:]) result.extend(right[j:]) return result def merge_sort(arr): if len(arr) <= 1: return arr mid = len(arr) // 2 left = merge_sort(arr[:mid]) right = merge_sort(arr[mid:]) return merge(left, right) # Example usage arr = [38, 27, 43, 3, 9, 82, 10] sorted_arr = merge_sort(arr) print(sorted_arr)
Dry Run
Let's trace the list [3, 1, 4, 2] through the merge sort code.
Split list
Split [3, 1, 4, 2] into [3, 1] and [4, 2]
Sort left half
Split [3, 1] into [3] and [1], both single elements, so return as is
Merge left half
Compare 3 and 1, merge into [1, 3]
Sort right half
Split [4, 2] into [4] and [2], both single elements, so return as is
Merge right half
Compare 4 and 2, merge into [2, 4]
Merge both halves
Compare [1, 3] and [2, 4], merge into [1, 2, 3, 4]
| Step | List State |
|---|---|
| Initial | [3, 1, 4, 2] |
| Split | [3, 1] and [4, 2] |
| Sort Left | [3] and [1] |
| Merge Left | [1, 3] |
| Sort Right | [4] and [2] |
| Merge Right | [2, 4] |
| Final Merge | [1, 2, 3, 4] |
Why This Works
Step 1: Divide the list
The code splits the list into two halves recursively until each sublist has one or zero elements, which are naturally sorted.
Step 2: Merge sorted halves
It then merges two sorted lists by comparing their first elements and building a new sorted list step by step.
Step 3: Combine results
This merging continues up the recursion chain until the entire list is merged back into a fully sorted list.
Alternative Approaches
def merge_sort_inplace(arr, start=0, end=None): if end is None: end = len(arr) if end - start > 1: mid = (start + end) // 2 merge_sort_inplace(arr, start, mid) merge_sort_inplace(arr, mid, end) left = arr[start:mid] right = arr[mid:end] i = j = 0 k = start while i < len(left) and j < len(right): if left[i] < right[j]: arr[k] = left[i] i += 1 else: arr[k] = right[j] j += 1 k += 1 while i < len(left): arr[k] = left[i] i += 1 k += 1 while j < len(right): arr[k] = right[j] j += 1 k += 1 arr = [5, 3, 8, 6, 2] merge_sort_inplace(arr) print(arr)
def merge_sort_bottom_up(arr): width = 1 n = len(arr) while width < n: for i in range(0, n, 2*width): left = arr[i:i+width] right = arr[i+width:i+2*width] arr[i:i+2*width] = merge(left, right) width *= 2 return arr arr = [7, 2, 5, 3, 9] print(merge_sort_bottom_up(arr))
Complexity: O(n log n) time, O(n) space
Time Complexity
Merge sort divides the list into halves recursively (log n splits) and merges all elements (n) at each level, resulting in O(n log n) time.
Space Complexity
It requires extra space to hold temporary lists during merging, so space complexity is O(n). In-place variants can reduce this.
Which Approach is Fastest?
Recursive merge sort is simple and fast for most cases; in-place and bottom-up variants save memory but add complexity.
| Approach | Time | Space | Best For |
|---|---|---|---|
| Recursive Merge Sort | O(n log n) | O(n) | Simplicity and clarity |
| In-place Merge Sort | O(n log n) | O(1) to O(n) | Memory-sensitive environments |
| Bottom-up Merge Sort | O(n log n) | O(n) | Avoiding recursion overhead |