Merge Sort
Brian E. Lavender
April 15, 2016
Merge Sort Concept
Sorts an array of values
- Divide and Conquer:
- Recursive Routine
- Complexity. O(Nlog2N)
- Disadvantage: auxiliary array
Merge two sorted halves Example
Two sorted arrays of size k/2.
Merge them to destination of size k.
min(4,8)?
Left Half (size 4)
Right Half (size 4)
Destination (size 8)
Merge Half Arrays To Destination (Step 1)
Move 4 from left_half to destination
min(15,8)?
Left Half (size 4)
Right Half (size 4)
Destination (size 8)
Merge Half Arrays To Destination (Step 2)
Move 8 from right_half to destination.
min(15,23)?
Left Half (size 4)
Right Half (size 4)
Destination (size 8)
Merge Half Arrays To Destination (Step 3)
Move 15 from left half to destination.
min(16,23)?
Left Half (size 4)
Right Half (size 4)
Destination (size 8)
Merge Half Arrays To Destination (Step 4)
Move 16 from left_half to destination.
min(50,23)?
Left Half (size 4)
Right Half (size 4)
Destination (size 8)
Merge Half Arrays To Destination (Step 5)
Move 23 from the right_half to destination.
min(50,42)?
Left Half (size 4)
Right Half (size 4)
Destination (size 8)
Merge Half Arrays To Destination (Step 6)
Move 42 from the right_half to destination.
min(50,108)?
Left Half (size 4)
Right Half (size 4)
Destination (size 8)
Merge Half Arrays To Destination (Step 7)
Move 50 from the left_half to destination.
empty(left_half)?
Left Half (size 4)
Right Half (size 4)
Destination (size 8)
Merge Half Arrays To Destination (Step 8)
Move 108 from the right_half to destination.
(have_items(left_array) or have_items(right_array))?
Left Half (size 4)
Right Half (size 4)
Destination (size 8)
Destination array sorted
8 steps linear time.
The C++ merge code
C++ Code illustrating the above merge. Consider four cases.
- Either left_half or right_half have elements to move to the destination.
- All elements from left_half moved to destination. Move minimum element from right_half.
- All elements from right_half moved to destination. Move minimum element from left_half.
- Move smaller element from left_half or right_half to destination.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| const int SIZE_HALF_LEFT = 4;
const int SIZE_HALF_RIGHT = 4;
const int SIZE_DEST = 8;
int left_half[SIZE_HALF_LEFT] = {4, 15, 16, 50};
int right_half[SIZE_HALF_RIGHT] = {8, 23, 42, 108};
int destination[SIZE_DEST];
// index counters
// i - left array , j - right array, k - destination array
i = 0; j = 0; k = 0;
// Merge the two half lists together
// while we have elements in either of the two lists
while (i < SIZE_HALF_LEFT || j < SIZE_HALF_RIGHT) {
// Completed the first half
if ( i >= SIZE_HALF_LEFT ) {
destination[k] = right_half[j];
j++;
}
// Completed the second half
else if (j >= SIZE_HALF_RIGHT) {
destination[k] = left_half[i];
i++;
}
// pick the smallest element from one
// of the two lists
else if (left_half[i] < right_half[j]) {
destination[k] = left_half[i];
i++;
} else {
destination[k] = right_half[j];
j++;
}
// increment our counter for destination
k++;
}
|
Next Challenge
Merge assumes left_half and right_half already sorted.
If we start with the following.
int destination[] = {108, 15, 50, 4, 8, 42, 23, 16};
Halves not sorted!
int left_half[] = {108, 15, 50, 4};
int right_half[] = {8, 42, 23, 16};
Keep dividing in half?
Get to the bottom of this
Repeatedly divide in half!
log28 = 3 levels
--- 8
^ 4 4
3 levels 2 2 2 2
v 1 1 1 1 1 1 1 1
---
Divide array into halves
Repeatedly call merge_sort on halves!
int destination[] = {108, 15, 50, 4, 8, 42, 23, 16};
First Division
Sorted? No
int left_half[] = {108, 15, 50, 4};
int right_half[] = {8, 42, 23, 16};
Second Division
Sorted? No
int left_half[] = {108, 15};
int right_half[] = {50, 4};
int left_half[] = {8, 42};
int right_half[] = {23, 16};
Third Division
Sorted? Yes
int left_half[] = {108};
int right_half[] = {15};
int left_half[] = {50};
int right_half[] = {4};
int left_half[] = {8};
int right_half[] = {42};
int right_half[] = {23};
int right_half[] = {16};
Merge two half arrays of size 1 to destination of size 2
Build it back up
min(23,16)?
Left Half (size 1)
Right Half (size 1)
Destination (size 2)
Merge two half arrays of size 1 to destination of size 2 (step 1)
Move 16 from right_half to destination
empty(right_half)?
Left Half (size 1)
Right Half (size 1)
Destination (size 2)
Merge two half arrays of size 1 to destination of size 2 (step 2)
Move 23 from first_half to destination
Left Half (size 1)
Right Half (size 1)
Destination (size 2)
We merged the simplest two half arrays of size 1 back into a dest array of 2 items.
Algorithm for recursion
- Figure out our base case
- Determine our recursive step.
Base Case
1 item already sorted.
void merge_sort(int destination[], int dest_size) {
// base case. Array size is one.
if (dest_size == 1)
return;
}
Recursive Step
- Make new left_half and right_half. Copy data from destination to halves.
- Call merge_sort on each half array.
- Merge the sorted halves together.
- Return the destination array
Divide the Array
Making new halves.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| void merge_sort(int destination[], int dest_size) {
int left_size, right_size;
if (dest_size == 1)
return;
else {
// ***********
// Make two new half arrays
// ***********
// Make two new arrays: left_array half and right_array half
// Integer division for size: 5 / 2 -> 2
size_left = dest_size / 2 ;
size_right = array_size - size_left;
int left_array[size_left];
int right_array[size_right];
for (i=0; i< size_left; i++)
left_array[i] = x[i];
for (i=0; i< size_right; i++)
right_array[i] = x[size_left + i];
// call merge_sort on left_array
// call merge_sort on right_array
// merge the left_array and right_array together
}
// return our sorted array
return;
}
|
Recursively Call Merge sort on halves
Recursively call merge_sort on the two halves.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| void merge_sort(int destination[], int dest_size) {
int left_size, right_size;
if (size == 1)
return;
else {
// Make two new arrays: left_array half and right_array half
// Integer division for size: 5 / 2 -> 2
size_left = dest_size / 2 ;
size_right = dest_size - size_left;
int left_array[size_left];
int right_array[size_right];
for (i=0; i< size_left; i++)
left_array[i] = x[i];
for (i=0; i< size_right; i++)
right_array[i] = x[size_left + i];
// **************
// Recursively Call Merge sort on halves
// **************
merge_sort(left_array,size_left);
merge_sort(right_array,size_right);
// Merge the two halves
// Our inductive step
}
// return our sorted array
return;
}
|
Side Step
Pause for a moment and make a function out our merge example previously illustrated.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| void merge(int left[], int right[], int destination[],
int size_of_left_half, int size_of_right_half) {
int i = 0; // works on left half
int j = 0; // works on right half
// The merge
while (i < size_of_left_half || j < size_of_right_half) {
// Completed the first half
if ( i >= size_of_left_half ) {
destination[k] = right[j];
j++;
}
// Completed the second half
else if (j >= size_of_right_half) {
destination[k] = left[i];
i++;
}
// pick the smallest one
else if (left_half[i] < right_half[j]) {
destination[k] = left[i];
i++;
} else {
destination[k] = right[j];
j++;
}
k++;
}
}
|
Merge the two halves
Merge left_half and right_half back to destination.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| void merge_sort(int destination[], int dest_size) {
int left_size, right_size;
if (dest_size == 1)
return;
else {
// Make left_array half and right_array half
size_left = dest_size / 2 ;
size_right = dest_size - size_left;
int left_array[size_left];
int right_array[size_right];
for (i=0; i< size_left; i++)
left_array[i] = destination[i];
for (i=0; i< size_right; i++)
right_array[i] = destination[size_left + i];
// recursively call merge_sort on the
// left_half and right_half
merge_sort(left_array,size_left);
merge_sort(right_array,size_right);
// **************
// Merge the two sorted halves
// Our inductive step
// **************
merge(left_array,right_array,destination,size_left,size_right);
}
// return our sorted array
return;
}
|
Final Code
The following is the entire merge sort program with a main driver. It also illustrates an array of an odd number of elements.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
| #include <iostream>
using namespace std;
void display(int x[], int size) {
int i;
cout << "Size is " << size << endl;
for (i=0; i < size; i++)
cout << x[i] << " ";
cout << endl;
}
void merge(int left[], int right[], int destination[],
int size_of_left_half, int size_of_right_half) {
int i = 0; // works on left half
int j = 0; // works on right half
int k = 0; // merged array count
// The merge
while (i < size_of_left_half || j < size_of_right_half) {
// Completed the first half
if ( i >= size_of_left_half ) {
destination[k] = right[j];
j++;
}
// Completed the second half
else if (j >= size_of_right_half) {
destination[k] = left[i];
i++;
}
// pick the smallest one
else if (left[i] < right[j]) {
destination[k] = left[i];
i++;
} else {
destination[k] = right[j];
j++;
}
k++;
}
}
// precondition
// size of the array is 1 or greater
void merge_sort(int destination[], int dest_size) {
int size_left = 0, size_right = 0;
int i;
// Our base case.
// An array of one element is considered sorted
if (dest_size == 1)
return ;
// recursively call merge_sort of two halves.
else {
size_left = dest_size / 2 ;
size_right = dest_size - size_left;
// temporary arrays for divide portion
int left_array[size_left];
int right_array[size_left];
// Copy data into new temp array for the left.
for (i=0; i< size_left; i++)
left_array[i] = destination[i];
// Copy data into new temp array for the right.
for (i=0; i< size_right; i++)
right_array[i] = destination[size_left + i];
// recursively call merge_sort on the
// left_half and right_half
merge_sort(left_array,size_left);
merge_sort(right_array,size_right);
// Merge the two halves
// Our inductive step
merge(left_array,right_array,destination,size_left,size_right);
}
return ;
}
int main()
{
const int SIZE_DEST = 11;
int destination[SIZE_DEST] = {101,99,21,12,11,25,20,4,2,17,3};
cout << "Before sort" << endl;
display(destination, SIZE_DEST);
merge_sort(destination, SIZE_DEST);
cout << "After sort" << endl;
display(destination, SIZE_DEST);
return 0;
}
|
Questions?!
Exercise
Randomly arrange the cups and see if you can perform the merge sort.
References
- Dale, Nell, C++ Plus Data Structures, 3rd Ed., Jones and Bartlett Publishers, 2003, pp 601-608.