Merge Sort Algorithm in DSA Python - Time & Space Complexity
We want to understand how the time taken by merge sort changes as the list to sort gets bigger.
Specifically, how many steps does merge sort take when sorting more items?
Analyze the time complexity of the following code snippet.
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)
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
This code splits the list into halves, sorts each half recursively, and then merges the sorted halves.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive splitting of the list and merging sorted halves.
- How many times: The list is split about log₂(n) times, and merging happens at each level over all elements.
Each time we split, the number of parts doubles, but each part is half the size. Merging all parts at each level touches all elements once.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 x 4 = 40 |
| 100 | About 100 x 7 = 700 |
| 1000 | About 1000 x 10 = 10,000 |
Pattern observation: Operations grow roughly as n times log n, meaning it grows faster than linear but much slower than quadratic.
Time Complexity: O(n log n)
This means if the list doubles in size, the time taken grows a bit more than double, but not as much as if it grew by the square of the size.
[X] Wrong: "Merge sort is just like bubble sort and takes a long time because it compares every pair repeatedly."
[OK] Correct: Merge sort smartly splits the list and merges sorted parts, so it avoids repeated comparisons over the whole list, making it much faster than bubble sort.
Understanding merge sort's time complexity shows you can analyze recursive algorithms and explain why they are efficient, a key skill in many coding discussions.
"What if we changed merge sort to split the list into three parts instead of two? How would the time complexity change?"
Practice
Merge Sort algorithm?Solution
Step 1: Understand the divide and conquer approach
Merge Sort splits the list into smaller parts to make sorting easier.Step 2: Merge sorted halves
After sorting each half, it combines them to form a fully sorted list.Final Answer:
Divide the list into halves, sort each half, then merge them -> Option BQuick Check:
Merge Sort = Divide, sort halves, merge [OK]
- Confusing Merge Sort with Bubble Sort
- Thinking it swaps elements directly without splitting
- Assuming it sorts in one pass
arr into two halves in Python for merge sort?Solution
Step 1: Use integer division for slicing
Python list slicing requires integer indices;len(arr)//2gives an integer midpoint.Step 2: Slice correctly for left and right halves
Left half is from start to midpoint (exclusive), right half from midpoint to end.Final Answer:
left = arr[:len(arr)//2]; right = arr[len(arr)//2:] -> Option CQuick Check:
Integer division and slicing = left = arr[:len(arr)//2]; right = arr[len(arr)//2:] [OK]
- Using single slash / causing float index error
- Incorrect slice ranges skipping elements
- Adding +1 unnecessarily in slicing
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)
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
print(merge_sort([4, 1, 3, 2]))Solution
Step 1: Recursively split and sort the list
The list [4,1,3,2] is split into [4,1] and [3,2], then further until single elements.Step 2: Merge sorted halves step-by-step
Merge [4] and [1] to get [1,4], merge [3] and [2] to get [2,3], then merge [1,4] and [2,3] to get [1,2,3,4].Final Answer:
[1, 2, 3, 4] -> Option AQuick Check:
Sorted list = [1, 2, 3, 4] [OK]
- Confusing merge order causing wrong output
- Not extending remaining elements after main loop
- Mixing up indices i and j
def merge(left, right):
result = []
i = j = 0
while i < len(left) or j < len(right):
if left[i] < right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
return resultSolution
Step 1: Analyze the while loop condition
Using 'or' means loop continues even if one list is exhausted, causing index out of range errors.Step 2: Correct condition and handle leftovers
The condition should be 'and' to compare elements safely, then extend remaining elements after loop.Final Answer:
Using 'or' instead of 'and' in while condition causes index errors -> Option AQuick Check:
While condition must be 'and' to avoid errors [OK]
- Using 'or' causing index out of range
- Not extending leftover elements after loop
- Forgetting to increment indices properly
[(3, 'c'), (1, 'a'), (2, 'b')] by the first element using merge sort. Which modification is needed in the merge function's comparison?Solution
Step 1: Understand tuple comparison
Tuples compare element-wise, but to sort by first element explicitly, compareleft[i][0]andright[j][0].Step 2: Modify merge comparison accordingly
Change the if condition in merge to compare the first elements of tuples for correct sorting.Final Answer:
Change comparison to if left[i][0] < right[j][0]: -> Option DQuick Check:
Compare tuple first elements for sorting [OK]
- Comparing whole tuples without index may work but is unclear
- Comparing second element instead of first
- Converting tuples to strings unnecessarily
Ready to prove it?
Pass the test and this pattern moves unknown
