Merge Sort as Divide and Conquer in DSA Javascript - Time & Space Complexity
We want to understand how the time taken by merge sort changes as the list size grows.
How does splitting and merging affect the total work done?
Analyze the time complexity of the following merge sort code.
function mergeSort(arr) {
if (arr.length <= 1) return arr;
const mid = Math.floor(arr.length / 2);
const left = mergeSort(arr.slice(0, mid));
const right = mergeSort(arr.slice(mid));
return merge(left, right);
}
function merge(left, right) {
const result = [];
let i = 0, j = 0;
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) result.push(left[i++]);
else result.push(right[j++]);
}
return result.concat(left.slice(i)).concat(right.slice(j));
}
This code splits the array into halves, sorts each half recursively, then merges the sorted halves.
- Primary operation: Recursively splitting the array and merging sorted halves.
- How many times: The array is split about log₂(n) times, and merging happens at each level over all elements.
Each split divides the array in half, creating about log₂(n) levels of splitting.
At each level, merging all parts touches all n elements once.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 * 4 = 40 |
| 100 | About 100 * 7 = 700 |
| 1000 | About 1000 * 10 = 10,000 |
Pattern observation: Operations grow roughly as n times log n, which grows faster than n but much slower than n squared.
Time Complexity: O(n log n)
This means the time grows a bit faster than the size of the input but much better than simple sorting methods that check every pair.
[X] Wrong: "Merge sort is just a simple loop over the array, so it must be O(n)."
[OK] Correct: Merge sort splits the array many times and merges at each level, so it does more work than a single pass.
Understanding merge sort's time complexity shows you how divide and conquer helps solve problems efficiently, a key skill in many coding challenges.
"What if we changed merge sort to split the array 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 array into two halves recursively until single elements remain.Step 2: Merge sorted halves
After splitting, it merges the halves back in sorted order to form the sorted array.Final Answer:
Divide the array into halves, sort each half, then merge them -> Option CQuick Check:
Merge Sort = Divide and conquer with merging [OK]
- Confusing Merge Sort with Bubble Sort
- Thinking Merge Sort uses a pivot like Quick Sort
- Believing Merge Sort sorts in place without merging
Solution
Step 1: Identify the stopping condition for recursion
Merge Sort stops splitting when the array has 1 or 0 elements, as these are already sorted.Step 2: Correct base case syntax
The checkif (array.length <= 1) return array;correctly returns the array when it can't be split further.Final Answer:
if (array.length <= 1) return array; -> Option AQuick Check:
Base case = array length ≤ 1 [OK]
- Returning null instead of the array
- Checking for length === 2 instead of ≤ 1
- Incorrectly returning array when length > 1
function mergeSort(arr) {
if (arr.length <= 1) return arr;
const mid = Math.floor(arr.length / 2);
const left = mergeSort(arr.slice(0, mid));
const right = mergeSort(arr.slice(mid));
return merge(left, right);
}
function merge(left, right) {
let result = [];
let i = 0, j = 0;
while (i < left.length && j < right.length) {
if (left[i] < right[j]) {
result.push(left[i]);
i++;
} else {
result.push(right[j]);
j++;
}
}
return result.concat(left.slice(i)).concat(right.slice(j));
}
console.log(mergeSort([4, 1, 3, 2]));Solution
Step 1: Trace the recursive splitting
The array [4,1,3,2] is split into [4,1] and [3,2], then further into 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 array = [1, 2, 3, 4] [OK]
- Confusing merge order and returning unsorted halves
- Forgetting to merge remaining elements after loop
- Incorrect slicing causing wrong splits
function mergeSort(arr) {
if (arr.length <= 1) return arr;
const mid = Math.floor(arr.length / 2);
const left = mergeSort(arr.slice(0, mid));
const right = mergeSort(arr.slice(mid));
return merge(left, right);
}
function merge(left, right) {
let result = [];
let i = 0, j = 0;
while (i < left.length || j < right.length) {
if (left[i] < right[j]) {
result.push(left[i]);
i++;
} else {
result.push(right[j]);
j++;
}
}
return result;
}Solution
Step 1: Analyze the merge loop condition
The loop useswhile (i < left.length || j < right.length), which can cause accessing undefined elements.Step 2: Correct loop condition for safe merging
It should bewhile (i < left.length && j < right.length)to compare valid elements, then append leftovers after the loop.Final Answer:
The while loop condition should use && instead of || -> Option BQuick Check:
Use && in merge loop condition [OK]
- Using || causes runtime errors accessing undefined
- Not appending leftover elements after loop
- Ignoring base case correctness
Solution
Step 1: Understand stable sorting in merge
Stable sorting means equal elements keep their original order after sorting.Step 2: Modify merge to preserve order
When left[i] equals right[j], choose left[i] first to keep its original position before right[j].Final Answer:
When elements are equal, always take the element from the left array first -> Option DQuick Check:
Stable merge = pick left element first on ties [OK]
- Picking right element first breaks stability
- Skipping duplicates loses data
- Random choice causes unstable sort
Ready to prove it?
Pass the test and this pattern moves unknown
