3 Merge Sort
3 Merge Sort
Merge Sort
Merge sort is based on the divide-and-conquer paradigm. Its worst-case running time has a
lower order of growth than insertion sort. Since we are dealing with sub-problems, we state each
sub-problem as sorting a sub-array A[p .. r]. Initially, p = 1 and r = n, but these values change as
we recurs through sub-problems.
1. Divide Step
If a given array A has zero or one element, simply return; it is already sorted.
Otherwise, split A[p .. r] into two sub-arrays A[p .. q] and A[q + 1 .. r], each
containing about half of the elements of A[p .. r]. That is, q is the halfway point
of A[p .. r].
2. Conquer Step
Conquer by recursively sorting the two sub-arrays A[p .. q] and A[q + 1 .. r].
3. Combine Step
Combine the elements back in A[p .. r] by merging the two sorted sub-
arrays A[p .. q] and A[q + 1 .. r] into a sorted sequence. To accomplish this step, we
will define a procedure MERGE (A, p, q, r).
Note that the recursion bottoms out when the sub-array has just one element, so that it is trivially
sorted.
To sort the entire sequence A[1 .. n], make the initial call to the procedure MERGE-SORT (A,
1, n).
1|P a g e
MERGE-SORT (A, p, r)
Merging
What remains is the MERGE procedure. The following is the input and output of the MERGE
procedure.
2|P a g e
INPUT: Array A and indices p, q, r such that p ≤ q ≤ r and sub-array A[p .. q] is
sorted and sub-array A[q + 1 .. r] is sorted. By restrictions on p, q, r, neither sub-
array is empty.
OUTPUT: The two sub-arrays are merged into a single sorted sub-array in A[p .. r].
We implement it so that it takes Θ(n) time, where n = r − p + 1, which is the number of elements
being merged.
Think of two piles of cards, each pile is sorted and placed face-up on a table with the smallest
cards on top. We will merge these into a single sorted pile, face-down on the table.
A basic step:
Each basic step should take constant time, since we check just the two top cards. There are at
most n basic steps, since each basic step removes one card from the input piles, and we started
with n cards in the input piles. Therefore, this procedure should take Θ(n) time.
Now the question is do we actually need to check whether a pile is empty before each basic
step?
The answer is no, we do not. Put on the bottom of each input pile a special sentinel card. It
contains a special value that we use to simplify the code. We use ∞, since that's guaranteed to
lose to any other value. The only way that ∞ cannot lose is when both piles have ∞ exposed as
their top cards. But when that happens, all the non-sentinel cards have already been placed into
the output pile. We know in advance that there are exactly r − p + 1 non-sentinel cards so stop
3|P a g e
once we have performed r − p + 1 basic steps. Never a need to check for sentinels, since they
will always lose. Rather than even counting basic steps, just fill up the output array from
index p up through and including index r .
MERGE (A, p, q, r )
1. n1 ← q − p + 1
2. n2 ← r − q
3. Create arrays L[1 . . n1 + 1] and R[1 . . n2 + 1]
4. FOR i ← 1 TO n1
5. DO L[i] ← A[p + i − 1]
6. FOR j ← 1 TO n2
7. DO R[j] ← A[q + j ]
8. L[n1 + 1] ← ∞
9. R[n2 + 1] ← ∞
10. i←1
11. j←1
12. FOR k ← p TO r
13. DO IF L[i ] ≤ R[ j]
14. THEN A[k] ← L[i]
15. i←i+1
16. ELSE A[k] ← R[j]
17. j←j+1
Example [from CLRS-Figure 2.3]: A call of MERGE(A, 9, 12, 16). Read the following figure
row by row. That is how we have done in the class.
4|P a g e
The first part shows the arrays at the start of the "for k ← p to r" loop, where A[p . . q] is
copied into L[1 . . n1 ] and A[q + 1 . . r ] is
copied into R[1 . . n2 ].
Succeeding parts show the situation at the start of successive iterations.
Entries in A with slashes have had their values copied to either L or R and have not had a
value copied back in yet. Entries in L and R with slashes have been copied back into A.
The last part shows that the subarrays are merged back into A[p . . r], which is now
sorted, and that only the sentinels (∞) are exposed in the arrays L and R.]
5|P a g e
6|P a g e
Running Time
The first two for loops (that is, the loop in line 4 and the loop in line 6) take Θ(n1 + n2 ) = Θ(n)
time. The last for loop (that is, the loop in line 12) makes n iterations, each taking constant time,
for Θ(n) time. Therefore, the total running time is Θ(n).
For simplicity, assume that n is a power of 2 so that each divide step yields two sub-problems,
both of size exactly n/2.
Divide: Just compute q as the average of p and r, which takes constant time i.e. Θ(1).
Conquer: Recursively solve 2 sub-problems, each of size n/2, which is 2T(n/2).
Combine: MERGE on an n-element sub-array takes Θ(n) time.
Summed together they give a function that is linear in n, which is Θ(n). Therefore, the recurrence
for merge sort running time is
By the master theorem in CLRS-Chapter 4 (page 73), we can show that this recurrence has the
solution
7|P a g e
Compared to insertion sort [Θ(n2 ) worst-case time], merge sort is faster. Trading a factor of n for
a factor of lg n is a good deal. On small inputs, insertion sort may be faster. But for large enough
inputs, merge sort will always be faster, because its running time grows more slowly than
insertion sorts.
Recursion Tree
We can understand how to solve the merge-sort recurrence without the master theorem. There is
a drawing of recursion tree on page 35 in CLRS, which shows successive expansions of the
recurrence.
The following figure (Figure 2.5b in CLRS) shows that for the original problem, we have a cost
of cn, plus the two subproblems, each costing T (n/2).
The following figure (Figure 2.5c in CLRS) shows that for each of the size-n/2 subproblems, we
have a cost of cn/2, plus two subproblems, each costing T (n/4).
8|P a g e
The following figure (Figure: 2.5d in CLRS) tells to continue expanding until the problem sizes
get down to 1.
9|P a g e
In the above recursion tree, each level has cost cn.
Mathematical Induction
Base case: n = 1
Inductive Step
Total cost is sum of costs at each level of the tree. Since we have lg n +1 levels, each costing cn,
the total cost is
cn lg n + cn.
10 | P a g e
Ignore low-order term of cn and constant coefÞcient c, and we have,
Θ(n lg n)
Implementation
{
m_sort(numbers, temp, 0, array_size - 1);
11 | P a g e
void m_sort(int numbers[], int temp[], int left, int right)
{
int mid;
}
void merge(int numbers[], int temp[], int left, int mid, int right)
12 | P a g e
left_end = mid - 1;
tmp_pos = left;
temp[tmp_pos] = numbers[left];
tmp_pos = tmp_pos + 1;
else
temp[tmp_pos] = numbers[mid];
tmp_pos = tmp_pos + 1;
13 | P a g e
mid = mid + 1;
temp[tmp_pos] = numbers[left];
left = left + 1;
tmp_pos = tmp_pos + 1;
temp[tmp_pos] = numbers[mid];
mid = mid + 1;
tmp_pos = tmp_pos + 1;
14 | P a g e
}
numbers[right] = temp[right];
right = right - 1;
15 | P a g e