### The Bottom-up Merge Sort Algorithm

• Operation of the Bottom-up merge sort

• Operation of the bottom-up merge sort algorithm:

• The bottom-up merge sort algorithm first merges pairs of adjacent arrays of 1 elements

• Then merges pairs of adjacent arrays of 2 elements

• And next merges pairs of adjacent arrays of 4 elements

• And so on....

 Until the whole array is merged

• Simple example

• The simplest example to understand the bottom-up merge sort is to use an array of length 2n (it merges perfectly)

• Example:

• Input array:

 ``` 6.4 3.5 7.5 2.5 8.9 4.2 9.2 1.1 ```

• Iteration 1:

• Merge pairs of adjacent arrays of size = 1:

• Iteration 2:

• Merge pairs of adjacent arrays of size = 2:

• Iteration 3:

• Merge pairs of adjacent arrays of size = 4:

• The whole array has been merged

Done !!!

• Important observation:

• In each iteration, every element in the input array a[ ] is involved in some merge operation

 Juts take a look at the 3 figures above.... Every element in the input array a[ ] has an arrow attached to it (That means the element in involved in some merge operation)

• We will use this observation at the end of this webpage to improve the merge sort slightly....

• The Simplified bottom-up Merge Sort algorithm

• Simplifying assumption:

 Assume that the number of elements in the input array is a power of 2 (= 2k)

• Let's figure out which array elements are merged in each iteration using an example

(So we can discover/understand the algorithm):

• In iteration 1:

 ``` Array elements merged with each other: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 .... (L) (R) (L) (R) (L) (R) (L) (R) (L) (R) (L) (R) (L) (R) a[0] with a[1] a[2] with a[3] ... (and so on) Each array has 1 element ```

Graphically:

• In iteration 2:

 ``` Array elements merged with each other: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 .... ----- ----- ----- ----- ----- ------- ------- (L) (R) (L) (R) (L) (R) (L) a[0] a[1] with a[2] a[3] a[4] a[5] with a[6] a[7] ... (and so on) Each array has 2 element ```

Graphically:

• In iteration 3:

 ``` Array elements merged with each other: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 .... ------------- ------------- --------------- ------- (L) (R) (L) (R) a[0] a[1] a[2] a[3] with a[4] a[5] a[6] a[7] a[8] a[9] a[10] a[11] with a[12] a[13] a[14] a[15] ... (and so on) Each array has 4 element ```

Conclusion:

• Iteration 1 with width = 1, the merge operations that are performed are:

 ``` merge( a, left = 0, middle = 1, right = 2 ) merge( a, left = 2, middle = 3, right = 4 ) merge( a, left = 4, middle = 5, right = 6 ) ... Or: merge( a, left = 0*width, middle = 1*width, right = 2*width ) merge( a, left = 2*width, middle = 3*width, right = 4*width ) merge( a, left = 4*width, middle = 5*width, right = 6*width ) ... ```

• Iteration 2 with width = 2 (new width = 2 * old width), the merge operations that are performed are:

 ``` merge( a, left = 0, middle = 2, right = 4 ) merge( a, left = 4, middle = 6, right = 8 ) merge( a, left = 8, middle = 10, right = 12 ) ... Or: merge( a, left = 0*width, middle = 1*width, right = 2*width ) merge( a, left = 2*width, middle = 3*width, right = 4*width ) merge( a, left = 4*width, middle = 5*width, right = 6*width ) ... ```

• Iteration 3 with width = 4 (new width = 2 * old width), the merge operations that are performed are:

 ``` merge( a, left = 0, middle = 4, right = 8 ) merge( a, left = 8, middle = 12, right = 16 ) ... Or: merge( a, left = 0*width, middle = 1*width, right = 2*width ) merge( a, left = 2*width, middle = 3*width, right = 4*width ) merge( a, left = 4*width, middle = 5*width, right = 6*width ) ... ```

• The Simplified bottom-up merge sorth algorithm in Java:

 ``` public static void sort(double[] a) { int width; for ( width = 1; width < a.length; width = 2*width ) { // Combine pairs of array a of width "width" int i; for ( i = 0; i < a.length; i = i + 2*width ) { int left, middle, right; left = i; middle = i + width; right = i + 2*width; merge( a, left, middle, right ); } } } ```

Note:

• The outer for-loop is as follows:

 ``` for ( width = 1; width < a.length; width = 2*width ) { // The variable width will take on these values: // iter 1: width = 1 (20) // iter 2: width = 2 (21) // iter 3: width = 4 (22) // and so on } ```

The variable width will always take one a value that is a power of 2 (= 2k)

• Therefore, the merge(a, left, middle, right):

 ``` for ( i = 0; i < a.length; i = i + 2*width ) { // i ONLY takes on value that are powers of 2 int left, middle, right; left = i; // left is a power of 2 middle = i + width; right = i + 2*width; // right is a power of 2 merge( a, left, middle, right ); } ```

• Since we have assumed that the number of elements in the input array is a power of 2 (= 2k), the merge operation:

 ``` merge( a, left, middle, right ); left = a power of 2 right = a power of 2 ```

will not use non-existing array elements !!!

 An example will be given in the next webpage The example will show that we will use non-existing array elements in the merge operation if a.length is not a power of 2

• Organization of the merge sort algorithm

• Recall:

• The merge() method needs to create a temporal array tmp[ ] to hold the merged elements

 If we make the merge() method create the array (as a local variable), the program will generate a lot of garbage

• It is preferred to do this:

 The user create the temporal array tmp[ ] and pass this array to the merge sort algorithm The merge sort in turn, will pass the temporal array tmp[ ] to the merge() method --- so merge() does not need to create the tmp[ ] array

• Preferred way to code merge sort:

 ``` public static void sort(double[] a, double[] tmp ) { int width; for ( width = 1; width < a.length; width = 2*width ) { // Combine pairs of array a of width "width" int i; for ( i = 0; i < a.length; i = i + 2*width ) { int left, middle, right; left = i; middle = i + width; right = i + 2*width; merge( a, left, middle, right, tmp ); } } } ```

• For completeness, I repeat the merge() method here:

 ``` /* ====================================================== Merge does this: 1. Merge 2 SORTED adjacent pieces of array a[ ] into a SORTED array tmp[ ]: left array (sorted) right array (sorted) a[iLeft ... iMiddle-1] a[iMiddle... iRight] \ / \ / sort \ / tmp[ iLeft ... iRight ] | | Copy back ! V a[ iLeft... iRight ] ` 2. Copy the merged result in tmp[ ] back to a[ ] The SAME portion of the array tmp[ ] is used !!! ====================================================== */ public static void Merge(double[] a, int iLeft, int iMiddle, int iRight, double[] tmp) { int i, j, k; i = iLeft; // Re-adjust the indices j = iMiddle; k = iLeft; while ( i < iMiddle || j < iRight ) // It's the same algorithm ! { if ( i < iMiddle && j < iRight ) { // Both array have elements if ( a[i] < a[j] ) tmp[k++] = a[i++]; else tmp[k++] = a[j++]; } else if ( i == iMiddle ) tmp[k++] = a[j++]; // a is empty else if ( j == iRight ) tmp[k++] = a[i++]; // b is empty } /* ================================= Copy tmp[] back to a[] ================================= */ for ( i = iLeft; i < iRight; i++ ) a[i] = tmp[i]; } ```

(It's the same one used in the top-down Merge Sort algorithm)

• Here's a test program to show how the bottom-up merge sort algorithm works:

 ``` public static void main( String[] args ) { double[] x = {6.4, 3.5, 7.5, 2.5, 8.9, 4.2, 9.2, 1.1} ; double[] help = new double[x.length]; // Create tmp for merge System.out.println("Before sort: " + Arrays.toString(x) ); MergeSort.sort( x, help ); // Merge sort System.out.println("\nAfter sort: " + Arrays.toString(x) ); } ```

Output: (I have highlighted the pieces of arrays that are merge with each other in each iteration)

 ```> java testProg Before sort: [6.4, 3.5, 7.5, 2.5, 8.9, 4.2, 9.2, 1.1] After 1 iter: [3.5, 6.4, 2.5, 7.5, 4.2, 8.9, 1.1, 9.2] After 1 iter: [2.5, 3.5, 6.4, 7.5, 1.1, 4.2, 8.9, 9.2] After 1 iter: [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] After sort: [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] ```

• Example Program: (Demo above code)

How to run the program:

 Right click on link and save in a scratch directory To compile:   javac testProg.java To run:          java testProgx

• Slight improvement to the Merge Sort algorithm

• Recall the important observation:

• In each iteration, every element in the input array a[ ] is involved in some merge operation

• Juts take a look at the 3 figures:

Every element in the input array a[ ] has an arrow attached to it

(That means the element in involved in some merge operation)

• We can use this observation to improve the merge sort slightly:

• Instead of copying the temporally array tmp[ ] back to a[ ] after each merge():

 ``` public static void Merge(double[] a, int iLeft, int iMiddle, int iRight, double[] tmp) { int i, j, k; i = iLeft; // Re-adjust the indices j = iMiddle; k = iLeft; while ( i < iMiddle || j < iRight ) // It's the same algorithm ! { if ( i < iMiddle && j < iRight ) { // Both array have elements if ( a[i] < a[j] ) tmp[k++] = a[i++]; else tmp[k++] = a[j++]; } else if ( i == iMiddle ) tmp[k++] = a[j++]; // a is empty else if ( j == iRight ) tmp[k++] = a[i++]; // b is empty } /* ======================================== Copy tmp[] back to a[] AFTER each merge ======================================== */ for ( i = iLeft; i < iRight; i++ ) a[i] = tmp[i]; } ```

We copy the temporally array tmp[ ] back to a[ ] after all merge() operations have been performed for a whole iteration:

 ``` public static void sort(double[] a, double[] tmp ) { int width; for ( width = 1; width < a.length; width = 2*width ) { // Combine pairs of array a of width "width" int i; for ( i = 0; i < a.length; i = i + 2*width ) { int left, middle, right; left = i; middle = i + width; right = i + 2*width; merge( a, left, middle, right, tmp ); // Merge } /* =============================================== All merge() are done for ONE complete iteration NOW we copy tmp[ ] back to a[ ] =============================================== */ for ( i = 0; i < a.length; i++ ) a[i] = tmp[i]; } } ```

• The slightly improved bottom-up merge sort algorithm:

 ``` public static void sort(double[] a, double[] tmp) { int width; for ( width = 1; width < a.length; width = 2*width ) { // Combine sections of array a of width "width" int i; for ( i = 0; i < a.length; i = i + 2*width ) { int left, middle, right; left = i; middle = i + width; right = i + 2*width; merge( a, left, middle, right, tmp ); } /* ======================================================== Instead of doing multiply copy operations inside merge() we perform ONE copy operation after ALL mereg operations ======================================================== */ for ( i = 0; i < a.length; i++ ) a[i] = tmp[i]; } } ```

with a slightly modified merge() method (we have moved the copy operation away):

 ``` public static void merge(double[] a, int iLeft, int iMiddle, int iRight, double[] tmp) { int i, j, k; i = iLeft; j = iMiddle; k = iLeft; while ( i < iMiddle || j < iRight ) { if ( i < iMiddle && j < iRight ) { // Both array have elements if ( a[i] < a[j] ) tmp[k++] = a[i++]; else tmp[k++] = a[j++]; } else if ( i == iMiddle ) tmp[k++] = a[j++]; // a is empty else if ( j == iRight ) tmp[k++] = a[i++]; // b is empty } // Copy step is removed ! } ```

• Example Program: (Demo above code)

How to run the program:

 Right click on link and save in a scratch directory To compile:   javac testProg.java To run:          java testProgx