Prelude to the merge sort algorithm

  • Consider the follow sort algorithm:

      Sorting an array a[ ] of n elements:
    
         1. split the array a[ ] into 2 halves:
    
               an array left[ ]  containing a[0] .. a[n/2]
    	   an array right[ ] containing a[n/2 + 1] .. a[n-1]
    
         2. Sort both halves of the arrays (left[ ] and right[ ])
    
         3. Merge the sorted arrays into the final sorted array
      


  • Dictatic note:

    • In this set of slides, I am presenting a simplified merge sort algorithm that uses SelectionSort( ) to sort the array portions

    • It's easier to understand than recursion...

Prelude to the merge sort algorithm

  • Example:

     Sort:       [6.4, 3.5, 7.5, 2.5, 8.9, 4.2, 9.2, 1.1]
                        /                    \
    		   /  split into 2 halves \
    		  /                        \
            [6.4, 3.5, 7.5, 2.5]        [8.9, 4.2, 9.2, 1.1]
                      |                         |
    		  |     Sort each half      | (E.g.: use SelectionSort) 
    		  V                         V
            [2.5, 3.5, 6.4, 7.5]        [1.1, 4.2, 8.9, 9.2]
                      \                        /
    		   \  Merge sorted halves /   (Use merge( ) !)
    		    \                    /
                 [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] 
     

Prelude to the merge sort algorithm

  • Example:

     Sort:       [6.4, 3.5, 7.5, 2.5, 8.9, 4.2, 9.2, 1.1]
                        /                    \
    		   /  split into 2 halves \
    		  /                        \
            [6.4, 3.5, 7.5, 2.5]        [8.9, 4.2, 9.2, 1.1] 
                      |                         |
    		  |     Sort each half      | (E.g.: use SelectionSort) 
    		  V                         V
            [2.5, 3.5, 6.4, 7.5]        [1.1, 4.2, 8.9, 9.2]
                      \                        /
    		   \  Merge sorted halves /   (Use merge( ) !)
    		    \                    /
                 [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] 
     

Prelude to the merge sort algorithm

  • Example:

     Sort:       [6.4, 3.5, 7.5, 2.5, 8.9, 4.2, 9.2, 1.1]
                        /                    \
    		   /  split into 2 halves \
    		  /                        \
            [6.4, 3.5, 7.5, 2.5]        [8.9, 4.2, 9.2, 1.1] 
                      |                         |
    		  |     Sort each half      | (E.g.: use SelectionSort) 
    		  V                         V
            [2.5, 3.5, 6.4, 7.5]        [1.1, 4.2, 8.9, 9.2]
                      \                        /
    		   \  Merge sorted halves /   (Use merge( ) !)
    		    \                    /
                 [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] 
     

Prelude to the merge sort algorithm

  • Example:

     Sort:       [6.4, 3.5, 7.5, 2.5, 8.9, 4.2, 9.2, 1.1]
                        /                    \
    		   /  split into 2 halves \
    		  /                        \
            [6.4, 3.5, 7.5, 2.5]        [8.9, 4.2, 9.2, 1.1] 
                      |                         |
    		  |     Sort each half      | (E.g.: use SelectionSort) 
    		  V                         V
            [2.5, 3.5, 6.4, 7.5]        [1.1, 4.2, 8.9, 9.2]
                      \                        /
    		   \  Merge sorted halves /   (Use merge( ) !)
    		    \                    /
                 [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] 
     

A simplified merge sort algorithm

 // Merge sorts the array elements A[s] ... A[e-1] using helper array H

 public static <T extends Comparable<T>> void sort(T[] A, int s, int e, T[] H)
 {
     if ( e - s <= 1 )      // Array of 0 or 1 element
         return;            // No need to sort an array of 1 element...

     int m = (e+s)/2;       // Middle
     This is NOT the actual Merge Sort algorithm....
     /* ------------------------------------------------
        "Easy to understand" merge sort:
             (1) sort the first half of the array
             (2) sort the 2nd half of the array
             (3) merge the 2 sorted portions
        ------------------------------------------------ */
     SelectionSort.sort(A, s, m);
     But it is very close to the real algorithm...
     SelectionSort.sort(A, m, e);

     // Merge both sorted arrays
     merge(A, s, m, e, H); // We have discussed merge() already...
 }
 

Note:   I will present an easy to understand (not recursive) version of the Merge sort algorithm

A simplified merge sort algorithm

 // Merge sorts the array elements A[s] ... A[e-1] using helper array H

 public static <T extends Comparable<T>> void sort(T[] A, int s, int e, T[] H)
 {
     if ( e - s <= 1 )      // A[s]..A[e] has 0 or 1 element
         return;            // No need to sort an array of 1 element...

     int m = (e+s)/2;       // Middle
   
     /* ------------------------------------------------
        "Easy to understand" merge sort:
             (1) sort the first half of the array
             (2) sort the 2nd half of the array
             (3) merge the 2 sorted portions
        ------------------------------------------------ */
     SelectionSort.sort(A, s, m);
    
     SelectionSort.sort(A, m, e);

     // Merge both sorted arrays
     merge(A, s, m, e, H); // We have discussed merge() already...
 }
 

Check for the base case:   if the portion that needs to be sorted has 1 element or less, we are done

A simplified merge sort algorithm

 // Merge sorts the array elements A[s] ... A[e-1] using helper array H

 public static <T extends Comparable<T>> void sort(T[] A, int s, int e, T[] H)
 {
     if ( e - s <= 1 )      // A[s]..A[e] has 0 or 1 element
         return;            // No need to sort an array of 1 element...

     int m = (e+s)/2;       // m = middle of s and e
   
     /* ------------------------------------------------
        "Easy to understand" merge sort:
             (1) sort the first half of the array
             (2) sort the 2nd half of the array
             (3) merge the 2 sorted portions
        ------------------------------------------------ */
     SelectionSort.sort(A, s, m);
    
     SelectionSort.sort(A, m, e);

     // Merge both sorted arrays
     merge(A, s, m, e, H); // We have discussed merge() already...
 }
 

Find the middle element:   m is the index of the array element that is the middle of A[s] and A[e-1]

A simplified merge sort algorithm

 // Merge sorts the array elements A[s] ... A[e-1] using helper array H

 public static <T extends Comparable<T>> void sort(T[] A, int s, int e, T[] H)
 {
     if ( e - s <= 1 )      // A[s]..A[e] has 0 or 1 element
         return;            // No need to sort an array of 1 element...

     int m = (e+s)/2;       // m = middle of s and e
   
     /* ------------------------------------------------
        "Easy to understand" merge sort:
             (1) sort the first half of the array
             (2) sort the 2nd half of the array
             (3) merge the 2 sorted portions
        ------------------------------------------------ */
     SelectionSort.sort(A, s, m);

     SelectionSort.sort(A, m, e);

     // Merge both sorted arrays
     merge(A, s, m, e, H); // We have discussed merge() already...
 }
 

Sort the first half:   in this simplified (easy to understand) version, we use the SelectionSort( ) to sort

A simplified merge sort algorithm

 // Merge sorts the array elements A[s] ... A[e-1] using helper array H

 public static <T extends Comparable<T>> void sort(T[] A, int s, int e, T[] H)
 {
     if ( e - s <= 1 )      // A[s]..A[e] has 0 or 1 element
         return;            // No need to sort an array of 1 element...

     int m = (e+s)/2;       // m = middle of s and e
   
     /* ------------------------------------------------
        "Easy to understand" merge sort:
             (1) sort the first half of the array
             (2) sort the 2nd half of the array
             (3) merge the 2 sorted portions
        ------------------------------------------------ */
     SelectionSort.sort(A, s, m);

     SelectionSort.sort(A, m, e);

     // Merge both sorted arrays
     merge(A, s, m, e, H); // We have discussed merge() already...
 }
 

Sort the 2nd half:   we will again use the SelectionSort( ) to sort

A simplified merge sort algorithm

 // Merge sorts the array elements A[s] ... A[e-1] using helper array H

 public static <T extends Comparable<T>> void sort(T[] A, int s, int e, T[] H)
 {
     if ( e - s <= 1 )      // A[s]..A[e] has 0 or 1 element
         return;            // No need to sort an array of 1 element...

     int m = (e+s)/2;       // m = middle of s and e
   
     /* ------------------------------------------------
        "Easy to understand" merge sort:
             (1) sort the first half of the array
             (2) sort the 2nd half of the array
             (3) merge the 2 sorted portions
        ------------------------------------------------ */
     SelectionSort.sort(A, s, m);

     SelectionSort.sort(A, m, e);

     // Merge both sorted arrays
     merge(A, s, m, e, H); // We have discussed merge() previously !
 }
 

Merge the 2 sorted protions:   we use the merge( ) algorithm discussed in the last set of slides

Test program for simplified mergeSort

    public static void main(String[] args)
    {
        Integer[] list = {4, 3, 2, 7, 1, 5, 8, 6};

        Integer[] H = new Integer[list.length];

        for(int i = 0; i < list.length; i++)
            System.out.print(list[i] + " ");
        System.out.println();

        MergeSort.sort(list, 0, list.length, H);;

        for (int i = 0; i < list.length; i++)
            System.out.print(list[i] + " ");
        System.out.println();
    }
  

DEMO: demo/14-sort/11-merge-sort/Demo.java + MergeSort.java + SelectionSort.java

The real merge sort algorithm

The easy to understand Merge Sort algorithm uses the Selection sort algorithm to sort the array portions:

 // Merge sorts the array elements A[s] ... A[e-1] using helper array H

 public static <T extends Comparable<T>> void sort(T[] A, int s, int e, T[] H)
 {
     if ( e - s <= 1 )      // A[s]..A[e] has 0 or 1 element
         return;            // No need to sort an array of 1 element...

     int m = (e+s)/2;       // m = middle of s and e
   
     /* ------------------------------------------------
        "Easy to understand" merge sort:
             (1) sort the first half of the array
             (2) sort the 2nd half of the array
             (3) merge the 2 sorted portions
        ------------------------------------------------ */
     SelectionSort.sort(A, s, m);

     SelectionSort.sort(A, m, e);

     // Merge both sorted arrays
     merge(A, s, m, e, H); // We have discussed merge() previously !
 } 

Because the Merge Sort algorithm can sort an array, we can use the merge sort algorithm instead !!!

The real merge sort algorithm

The real Merge Sort algorithm is a recursive (= divide and conquer) sorting algorithm:

 // Merge sorts the array elements A[s] ... A[e-1] using helper array H

 public static <T extends Comparable<T>> void sort(T[] A, int s, int e, T[] H)
 {
     if ( e - s <= 1 )      // A[s]..A[e] has 0 or 1 element
         return;            // No need to sort an array of 1 element...

     int m = (e+s)/2;       // m = middle of s and e
   
     /* ------------------------------------------------
        The actual merge sort:
             (1) sort the first half of the array using MergeSort
             (2) sort the 2nd half of the array using MergeSort
             (3) merge the 2 sorted portions
        ------------------------------------------------ */
     MergeSort.sort(A, s, m, H);

     MergeSort.sort(A, m, e, H);

     // Merge both sorted arrays
     merge(A, s, m, e, H); // We have discussed merge() previously !
 } 

I will illustrate the recursive Merge Sort algorithm with diagrams next...

How the mergeSort algorithm works

Consider the mergeSort( ) algorithm on the following input:   the parameters are s=0 and e=8

 mergeSort(0,8)

How the mergeSort algorithm works

mergeSort(0,8) first split the input array into 2 halves:   m = (8+0)/2 = 4

 mergeSort(0,8)

How the mergeSort algorithm works

mergeSort(0,8) then recurses and call mergeSort(0,4) to sort the left half of the input array:

 mergeSort(0,8) ->  mergeSort(0,4)

How the mergeSort algorithm works

The new mergeSort(0,4) first split the input array into 2 halves:   m = (4+0)/2 = 2

 mergeSort(0,8) ->  mergeSort(0,4)

How the mergeSort algorithm works

This mergeSort(0,4) then recurses can call mergeSort(0,2) and sort the left half of the array:

 mergeSort(0,8) ->  mergeSort(0,4) ->  mergeSort(0,2)

How the mergeSort algorithm works

The new mergeSort(0,2) first split the input array into 2 halves:   m = (2+0)/2 = 1

 mergeSort(0,8) ->  mergeSort(0,4) ->  mergeSort(0,2)

How the mergeSort algorithm works

This mergeSort(0,2) then recurses and call mergeSort(0,1) sort the left half of the array:

 mergeSort(0,8) ->  mergeSort(0,4) ->  mergeSort(0,2) ->  mergeSort(0,1)

How the mergeSort algorithm works

The input array consists of 1 element (= base case) and mergeSort(0,1) returns to mergeSort(0,2)

 mergeSort(0,8) ->  mergeSort(0,4) ->  mergeSort(0,2) <-  

How the mergeSort algorithm works

The mergeSort(0,2) then recurses and call mergeSort(1,2) to sort the right half of the array:

 mergeSort(0,8) ->  mergeSort(0,4) ->  mergeSort(0,2) ->  mergeSort(1,2) 

How the mergeSort algorithm works

The input array consists of 1 element (= base case) and mergeSort(1,2) returns

 mergeSort(0,8) ->  mergeSort(0,4) ->  mergeSort(0,2) <-- both halves sorted

How the mergeSort algorithm works

mergeSort(0,2) will finally merge the 2 sorted arrays and return to the caller mergeSort(0.4):

 mergeSort(0,8) ->  mergeSort(0,4) <--- left portion is sorted

How the mergeSort algorithm works

This mergeSort(0,4) then recurses and call mergeSort(2,4) to sort the right half of the array:

 mergeSort(0,8) ->  mergeSort(0,4) ->  mergeSort(2,4)  (and so on)

How the mergeSort algorithm works

MergeSort(2,4) will repeat the previously shown steps (omitted) - the end result is it sorts the input array:

 The same steps are repeated for the right array..

How the mergeSort algorithm works

The 2nd level mergeSort(0,4) will merge the 2 sorted arrays and ...

 left and right halves are sorted and then merged...

How the mergeSort algorithm works

The 2nd level mergeSort(0,4) will merge the 2 sorted arrays and return to the top level mergeSort(0,8):

 The 2nd level Merge Sort will return to the top level Merge sort...

How the mergeSort algorithm works

The top level mergeSort(0,8) then recurses and call mergeSort(4,8) to sort the right half of the array:

 The top level Merge Sort will repeat the steps for the right half of the array...

How the mergeSort algorithm works

mergeSort(4,8) will repeat the previously shown steps (omitted) - the end result is it sorts the right array:

 The result is: the right half of the array is sorted...

How the mergeSort algorithm works

Finally, the top level mergeSort(0,8) will have 2 sorted arrays:

 The final result: the entire array is sorted !

How the mergeSort algorithm works

The top level mergeSort(0,8) merges the 2 sorted array and obtains the final result:

 Next: Demo merge sort 

Demo program for Merge Sort

    public static void main(String[] args)
    {
        Integer[] list = {6, 3, 8, 2, 5, 1, 7, 4};

        Integer[] H = new Integer[list.length];   // Helper array for Merge

        System.out.println("\nInput:");
        for (int i = 0; i < list.length; i++)
            System.out.print(list[i] + " ");
        System.out.println("\n");

        MergeSort.sort(list, 0, list.length, H);

        System.out.println("\nResult:");
        for (int i = 0; i < list.length; i++)
            System.out.print(list[i] + " ");
        System.out.println();
    }

DEMO: demo/14-sort/12-merge-sort/Demo.java + MergeSort.java

Additional properties of Merge sort algorithm

  • Recall: In-place

    • A sorting algorithm is in-place if it does not require another array to execute the algorithm

    The Merge Sort algorithm requires the use of an another array to perform the mereg operation:

    • Merge sort is not an in-place sorting algorithm


  • Recall:   Stable:

    • A sorting algorithm is stable if it preserves the relative order of equal keys in the array

    The Merge Sort algorithm is stable

    (Because it merge the array halves, the same keys in the left half are merged first)