MergeSort
Problem and Approach
- Want to sort a list of numbers
- Sort each half of the list
- Merge sorted halves together into sorted whole
- Recursive algorithm plus "helper" merge algorithm
- Base case: only one element in list; is sorted
The Algorithm(s)
void MergeSort( float array[], int start_pos, int end_pos)
{
if ( start_pos == end_pos ) // Only one element
return;
int middle_pos = ( start_pos + end_pos ) / 2; // Divide in half
MergeSort( array, start_pos, middle_pos); // Sort left half
MergeSort( array, middle_pos + 1, end_pos); // Sort right half
Merge( array, start_pos, middle_pos, end_pos); // Merge together
}
void Merge( float array[], int start_pos, int middle_pos, int end_pos)
{
float copy_array[end_pos - start_pos + 1]; // Copy elements -- NOT STANDARD-COMPLIANT!
for( int i = start_pos; i <= end_pos; i++) // Will sort into parameter array
copy_array[ i - start_pos ] = array[i];
int left = 0, right = middle_pos + 1 - start_pos; // Keep track of where we are in each half
int index = start_pos; // Keep track of how much of sorted array has been filled in
while ( left <= middle_pos - start_pos && right <= end_pos - start_pos )
{
if( copy_array[left] < copy_array[right] ) // Element in left is smaller; goes first
array[index++] = copy_array[left++];
else // Element in right is smaller; goes first
array[index++] = copy_array[right++];
}
// Out of loop; either left = middle_pos + 1 (sorted all left elements into array)
// or right = end_pos + 1 (sorted all right elements into array).
// Now: copy remaining leftover elements from appropriate half into array.
// Only one of these loops will actually execute; the other will fail the first check
while ( right <= end_pos - start_pos )
array[index++] = copy_array[right++];
while ( left <= middle_pos - start_pos )
array[index++] = copy_array[left++];
}
Why does this work?
Correctness of MergeSort
- Correct on one element -- returns with no further sorting
- More than one element -- sort each half (closer to base case)
- Assuming Merge works correctly, merged array will be sorted
- We are really passing off all the work of sorting into Merge
Correctness of Merge
- Left and right array halves are sorted (assume!)
- Minimum of whole array either lies on left or right
- Will be minimum of its half, so first in its half
- Whichever minimum is smaller is minimum of whole array
- Copy into first element of sorted array, then skip over it next time
- Now second-minimum of whole array either lies on left or right, etc.
- Continue in this fashion until left or right side is completely used up
- Then just copy remaining elements from other half onto end of sorted array
Runtime Analysis
- Must analyze both Merge and MergeSort
- Merge:
n-long for loop to copy: O(n)
main while loop: perform several O(1) operations each iteration
at most n iterations, since one array element copied each time
so, O(n) for this loop
last while loop (only one of two): perform several O(1) operations each iteration
at most n iterations, as above
so, O(n) for this loop also
Total Running Time: O(n) + O(n) + O(n) = O(n), on an n-long input array
- MergeSort:
make 2 recursive calls: O(1) (count runtime of recursive calls separately)
call Merge: O(array size)
top-level call: O(1) + O(n) = O(n)
2 second-level calls: O(1) + O(n/2) = O(n/2) each, so O(n) total
4 third-level calls: O(1) + O(n/4) = O(n/4) each, so O(n) total -- etc.
Each level of call takes O(n); how many levels?
How many times can we divide an n-long array in half? lg(n) !
So, lg(n) levels * O(n) time each = O(n lg n)
Total Running Time: O(n lg n), on an n-long input array
A Word about Efficiency
- O(n lg n) is optimal sort time
- Can only beat with information about elements (within a certain range, etc.)
- Proof: beyond the scope of this class!