Merging 2 sorted portions of an array

  • You are given two sorted array portions that are adjacent to each other:

     Input:
    
       A[]:   ....... A[s] ... A[m-1]  A[m] ... A[e-1] ....
                      <------------->  <------------->
                          Sorted            Sorted 

  • Design an efficient algorithm to merge the 2 sorted array portions
    The result must occupy the same portion in the array

    Explained in diagram:

     Output:
    
       A[]:   ....... A[s] ... A[m-1]  A[m] ... A[e-1] ....
                      <------------------------------>
                              Sorted 

Merging 2 sorted portions of an array

  • Example:

     Input:
    
       A[]:   2    9    4    6    7    3    5    8   10   1
                        <--------->    <--------->
                          Sorted          Sorted  

  • After merging the sorted portions of the array, the output is:

     Output:
    
       A[]:   2    9    3    4    5    6    7    8   10   1
                        <------------------------>
                              Sorted 

Parameters of the merge algorithm

  • The merge algorithm has 3 (index) parameters: s, m and e.

    They are explained in the diagram below.

  • s = (start) the start index of the first sorted portion
    m = (middle) the start index of the second sorted portion
    e = (end) the end index of the second sorted portion

     indexes in first  sorted portion = s, s+1, ... m-1
     indexes in second sorted portion = m, m+1, ... e-1
                     
     A[ ] =  ....    4   6   7   3   5   8   .....    
                     ^           ^           ^
    		 |           |           |
                     s           m           e 
    

  • The merge algorithm will only merge the array elements inside A[s]..A[e−1]

    The merge algorithm will not affect the array elements outside the range

Variables used in the merge algorithm

  • We use 2 indexes i and j to point to the current elements in each sorted array portion:

                    i=s         j=m
                     |           |
                     V           V
     A[ ] =  ....    4   6   7   3   5   8   .....    
                     ^           ^           ^
    		 |           |           |
                     s           m           e 
    
     H[ ] = helper array to perform merging
    

    The element A[i] is always the smallest value in the left (sorted) portion

    The element A[j] is always the smallest value in the right (sorted) portion

  • We use a helper array variable H[] array to perform the merge operation:

    • We will repeatedly copy the smallest value from both arrays to H[]

Illustrating the merge algorithm

  • Current state:

                    i=s         j=m
                     |           |
                     V           V
     A[ ] =  ....    4   6   7   3   5   8   .....    
                     ^           ^           ^
    		 |           |           |
                     s           m           e 
    
     H[ ] =
    

    Compare A[i] and A[j]:

    • Since A[j] < A[i]:

      • We copy A[j] to H[ ]          and

      • increment j

Illustrating the merge algorithm

  • Current state:

                    i=s             j=m+1
                     |               |
                     V               V
     A[ ] =  ....    4   6   7   3   5   8   .....    
                     ^           ^           ^
    		 |           |           |
                     s           m           e 
    
     H[ ] =  3
    

    Compare A[i] and A[j]:

    • Since A[i] < A[j]:

      • We copy A[i] to H[ ]          and

      • increment i

Illustrating the merge algorithm

  • Current state:

                        i=s+1       j=m+1
                         |           |
                         V           V
     A[ ] =  ....    4   6   7   3   5   8   .....    
                     ^           ^           ^
    		 |           |           |
                     s           m           e 
    
     H[ ] =  3  4
    

    Compare A[i] and A[j]:

    • Since A[j] < A[i]:

      • We copy A[j] to H[ ]          and

      • increment j

Illustrating the merge algorithm

  • Current state:

                        i=s+1           j=m+2
                         |               |
                         V               V
     A[ ] =  ....    4   6   7   3   5   8   .....    
                     ^           ^           ^
    		 |           |           |
                     s           m           e 
    
     H[ ] =  3  4  5
    

    Compare A[i] and A[j]:

    • Since A[i] < A[j]:

      • We copy A[i] to H[ ]          and

      • increment i

Illustrating the merge algorithm

  • Current state:

                            i=s+2       j=m+2
                             |           |
                             V           V
     A[ ] =  ....    4   6   7   3   5   8   .....    
                     ^           ^           ^
    		 |           |           |
                     s           m           e 
    
     H[ ] =  3  4  5  6
    

    Compare A[i] and A[j]:

    • Since A[i] < A[j]:

      • We copy A[i] to H[ ]          and

      • increment i

Illustrating the merge algorithm

  • Current state:

                                i=m     j=m+2
                                 |       |
                                 V       V
     A[ ] =  ....    4   6   7   3   5   8   .....    
                     ^           ^           ^
    		 |           |           |
                     s           m           e 
    
     H[ ] =  3  4  5  6  7
    

    We detected the "left array portion exhausted" condition: i == m :

    • Since i == m, the left portion is exhausted:

      • If the right portion has items: we copy A[j] to H[ ]...

      • increment j

Illustrating the merge algorithm

  • Current state:

                                i=m         j=e
                                 |           |
                                 V           V
     A[ ] =  ....    4   6   7   3   5   8   .....    
                     ^           ^           ^
    		 |           |           |
                     s           m           e 
    
     H[ ] =  3  4  5  6  7  8
    

    We detected the termination condition: i == m and j == e :

    • Since i == m and j == e:   both portions are exhausted !!

      • H[ ] now contains the merged result

      • We now copy H[ ] back to A[ ]...

Illustrating the merge algorithm

  • Final state:

                                
                                
                                
     A[ ] =  ....    3   4   5   6   7   8   .....    
                     ^           ^           ^
    		 |           |           |
                     s           m           e 
    
     H[ ] =  3  4  5  6  7  8
    

    After copying H[ ] back to A[ ].

The merge algorithm that merges 2 sorted array portions

We now write the merge( ) algorithm that merges 2 sorted array portions:

 public static <T extends Comparable<T>> void merge(T[] A, int s, int m, int e, T[] H)
 {
        int i = s, j = m;  // Current elements in left and right portions
        int k = 0;         // Current copy location in help array H

       
      The merge( ) method will be invoked repeatedly 
         
      If we create a helper array inside merge( )
      we would repeatedly allocate and de-allocate memory
    
      That is inefficient    
   
      So we create the helper array in main( ) once
      and pass it to merge() as parameter H[ ]  
           
            else if ( j == e )     // Right part is exhausted (empty)
                H[k++] = A[i++];
        }

        // Copy H[ ] back to A[ ]
        for (i = s, k = 0; i < e; i++, k++)
            A[i] = H[k];
 } 

The merge algorithm that merges 2 sorted array portions

We first define and initialize the indexes that points to the current elements in the sorted portions:

 public static <T extends Comparable<T>> void merge(T[] A, int s, int m, int e, T[] H)
 {
     int i = s, j = m;  // Current elements in left and right (sorted) portions
     int k = 0;         // Current copy location in help array H

     while ( i < m || j < e ) // Loop as long as there are unprocessed items
     {
         if ( i < m && j < e )
         {  // Both arrays have unprocessed elements
             if ( A[i].compareTo(A[j]) <= 0 )
                 H[k++] = A[i++];
             else
                 H[k++] = A[j++];
         }
         else if ( i == m )     // Left part is exhausted (empty)
             H[k++] = A[j++];
         else if ( j == e )     // Right part is exhausted (empty)
             H[k++] = A[i++];
     }

     // Copy H[ ] back to A[ ]
     for (i = s, k = 0; i < e; i++, k++)
         A[i] = H[k];
 } 

The merge algorithm that merges 2 sorted array portions

We define and initialize the index k that points to the output location in the helper array H[ ]:

 public static <T extends Comparable<T>> void merge(T[] A, int s, int m, int e, T[] H)
 {
     int i = s, j = m;  // Current elements in left and right (sorted) portions
     int k = 0;         // Current copy location in helper array H

     while ( i < m || j < e ) // Loop as long as there are unprocessed items
     {
         if ( i < m && j < e )
         {  // Both arrays have unprocessed elements
             if ( A[i].compareTo(A[j]) <= 0 )
                 H[k++] = A[i++];
             else
                 H[k++] = A[j++];
         }
         else if ( i == m )     // Left part is exhausted (empty)
             H[k++] = A[j++];
         else if ( j == e )     // Right part is exhausted (empty)
             H[k++] = A[i++];
     }

     // Copy H[ ] back to A[ ]
     for (i = s, k = 0; i < e; i++, k++)
         A[i] = H[k];
 } 

The merge algorithm that merges 2 sorted array portions

We repeat as long as one of the array portion still has an unprocessed element:

 public static <T extends Comparable<T>> void merge(T[] A, int s, int m, int e, T[] H)
 {
     int i = s, j = m;  // Current elements in left and right (sorted) portions
     int k = 0;         // Current copy location in helper array H

     while ( i < m || j < e ) // Loop as long as there are unprocessed items
     {
         if ( i < m && j < e )
         {  // Both arrays have unprocessed elements
             if ( A[i].compareTo(A[j]) <= 0 )
                 H[k++] = A[i++];
             else
                 H[k++] = A[j++];
         }
         else if ( i == m )     // Left part is exhausted (empty)
             H[k++] = A[j++];
         else if ( j == e )     // Right part is exhausted (empty)
             H[k++] = A[i++];
     }

     // Copy H[ ] back to A[ ]
     for (i = s, k = 0; i < e; i++, k++)
         A[i] = H[k];
 } 

The merge algorithm that merges 2 sorted array portions

We must handle 3 cases:

 public static <T extends Comparable<T>> void merge(T[] A, int s, int m, int e, T[] H)
 {
     int i = s, j = m;  // Current elements in left and right (sorted) portions
     int k = 0;         // Current copy location in helper array H

     while ( i < m || j < e ) // Loop as long as there are unprocessed items
     {
	 case 1:   both (sorted) portions have unprocessed elements
     






	 case 2:  the left  portion is exhausted
              
         case 3:  the right portion is exhausted
     }

     // Copy H[ ] back to A[ ]
     for (i = s, k = 0; i < e; i++, k++)
         A[i] = H[k];
 } 

The merge algorithm that merges 2 sorted array portions

Case 1:   how to detect when both arrays have unprocessed elements:

 public static <T extends Comparable<T>> void merge(T[] A, int s, int m, int e, T[] H)
 {
     int i = s, j = m;  // Current elements in left and right (sorted) portions
     int k = 0;         // Current copy location in helper array H

     while ( i < m || j < e ) // Loop as long as there are unprocessed items
     {
         if ( i < m && j < e )
         {  // Both portions have unprocessed elements
             if ( A[i].compareTo(A[j]) <= 0 )
                 H[k++] = A[i++];
             else
                 H[k++] = A[j++];
         }
         else if ( i == m )     // Left part is exhausted (empty)
             H[k++] = A[j++];
         else if ( j == e )     // Right part is exhausted (empty)
             H[k++] = A[i++];
     }

     // Copy H[ ] back to A[ ]
     for (i = s, k = 0; i < e; i++, k++)
         A[i] = H[k];
 } 

The merge algorithm that merges 2 sorted array portions

Case 1:   if A[i] <= A[j], we copy A[i] over to H[k] and increment   i   and  k:

 public static <T extends Comparable<T>> void merge(T[] A, int s, int m, int e, T[] H)
 {
     int i = s, j = m;  // Current elements in left and right (sorted) portions
     int k = 0;         // Current copy location in helper array H

     while ( i < m || j < e ) // Loop as long as there are unprocessed items
     {
         if ( i < m && j < e )
         {  // Both portions have unprocessed elements
             if ( A[i].compareTo(A[j]) <= 0 )
                 H[k] = A[i];
		 i++;
		 k++;
         }
         else if ( i == m )     // Left part is exhausted (empty)
             H[k++] = A[j++];
         else if ( j == e )     // Right part is exhausted (empty)
             H[k++] = A[i++];
     }

     // Copy H[ ] back to A[ ]
     for (i = s, k = 0; i < e; i++, k++)
         A[i] = H[k];
 } 

The merge algorithm that merges 2 sorted array portions

Shorthand:   if we increase a variable by 1 after we used the variable, we can use var++ for short:

 public static <T extends Comparable<T>> void merge(T[] A, int s, int m, int e, T[] H)
 {
     int i = s, j = m;  // Current elements in left and right (sorted) portions
     int k = 0;         // Current copy location in helper array H

     while ( i < m || j < e ) // Loop as long as there are unprocessed items
     {
         if ( i < m && j < e )
         {  // Both portions have unprocessed elements
             if ( A[i].compareTo(A[j]) <= 0 )
                 H[k++] = A[i++];
             else
                 H[k++] = A[j++];
         }
         else if ( i == m )     // Left part is exhausted (empty)
             H[k++] = A[j++];
         else if ( j == e )     // Right part is exhausted (empty)
             H[k++] = A[i++];
     }

     // Copy H[ ] back to A[ ]
     for (i = s, k = 0; i < e; i++, k++)
         A[i] = H[k];
 } 

The merge algorithm that merges 2 sorted array portions

Case 1 continued:   if A[i] > A[j], we copy A[j] over to H[k] and increment  j  and  k:

 public static <T extends Comparable<T>> void merge(T[] A, int s, int m, int e, T[] H)
 {
     int i = s, j = m;  // Current elements in left and right (sorted) portions
     int k = 0;         // Current copy location in helper array H

     while ( i < m || j < e ) // Loop as long as there are unprocessed items
     {
         if ( i < m && j < e )
         {  // Both portions have unprocessed elements
             if ( A[i].compareTo(A[j]) <= 0 )
                 H[k++] = A[i++];
             else
                 H[k++] = A[j++];
         }
         else if ( i == m )     // Left part is exhausted (empty)
             H[k++] = A[j++];
         else if ( j == e )     // Right part is exhausted (empty)
             H[k++] = A[i++];
     }

     // Copy H[ ] back to A[ ]
     for (i = s, k = 0; i < e; i++, k++)
         A[i] = H[k];
 } 

The merge algorithm that merges 2 sorted array portions

Case 2:   if the left portion of the array is exhausted, we copy A[j] over to H[k] (and increment  j  and  k):

 public static <T extends Comparable<T>> void merge(T[] A, int s, int m, int e, T[] H)
 {
     int i = s, j = m;  // Current elements in left and right (sorted) portions
     int k = 0;         // Current copy location in helper array H

     while ( i < m || j < e ) // Loop as long as there are unprocessed items
     {
         if ( i < m && j < e )
         {  // Both portions have unprocessed elements
             if ( A[i].compareTo(A[j]) <= 0 )
                 H[k++] = A[i++];
             else
                 H[k++] = A[j++];
         }
         else if ( i == m )     // Left part is exhausted (empty)
             H[k++] = A[j++];
         else if ( j == e )     // Right part is exhausted (empty)
             H[k++] = A[i++];
     }

     // Copy H[ ] back to A[ ]
     for (i = s, k = 0; i < e; i++, k++)
         A[i] = H[k];
 } 

The merge algorithm that merges 2 sorted array portions

Case 3:   if the right portion of the array is exhausted, we copy A[i] over to H[k] (and increment  i  and  k):

 public static <T extends Comparable<T>> void merge(T[] A, int s, int m, int e, T[] H)
 {
     int i = s, j = m;  // Current elements in left and right (sorted) portions
     int k = 0;         // Current copy location in helper array H

     while ( i < m || j < e ) // Loop as long as there are unprocessed items
     {
         if ( i < m && j < e )
         {  // Both portions have unprocessed elements
             if ( A[i].compareTo(A[j]) <= 0 )
                 H[k++] = A[i++];
             else
                 H[k++] = A[j++];
         }
         else if ( i == m )     // Left part is exhausted (empty)
             H[k++] = A[j++];
         else if ( j == e )     // Right part is exhausted (empty)
             H[k++] = A[i++];
     }

     // Copy H[ ] back to A[ ]
     for (i = s, k = 0; i < e; i++, k++)
         A[i] = H[k];
 } 

The merge algorithm that merges 2 sorted array portions

Finally:   copy the merged result in H[ ] (starting at index 0) back to A[ ] (starting at index s):

 public static <T extends Comparable<T>> void merge(T[] A, int s, int m, int e, T[] H)
 {
     int i = s, j = m;  // Current elements in left and right (sorted) portions
     int k = 0;         // Current copy location in helper array H

     while ( i < m || j < e ) // Loop as long as there are unprocessed items
     {
         if ( i < m && j < e )
         {  // Both portions have unprocessed elements
             if ( A[i].compareTo(A[j]) <= 0 )
                 H[k++] = A[i++];
             else
                 H[k++] = A[j++];
         }
         else if ( i == m )     // Left part is exhausted (empty)
             H[k++] = A[j++];
         else if ( j == e )     // Right part is exhausted (empty)
             H[k++] = A[i++];
     }

     // Copy H[ ] back to A[ ]
     for (i = s, k = 0; i < e; i++, k++)
         A[i] = H[k];
 } 

Test program

    public static void main(String[] args)
    {
        Integer[] list = {2, 9, 4, 6, 7, 3, 5, 8, 10, 1};
                         //     <----->  <----->
                         //      sorted   sorted

        Integer[] help = new Integer[list.length];      // Helper array

        printArray(list);

        MergeSort.merge(list, 2, 5, 8, help);

        printArray(list);
    }

    public static  void printArray(T[] A)
    {
        for(int i = 0; i < A.length; i++)
            System.out.print(A[i] + " ");
        System.out.println();
    }

DEMO: 14-sort/10-merge-sort/Demo.java

How to sort arrays using the merge( ) method

An array of 1 element is sorted !!
By merging 2 arrays of 1 elements, we get a sorted portion of size = 2
 
 
Here is how to sort an array of 2 elements:

    public static void main(String[] args)
    {
        Integer[] list = {7, 3};

        Integer[] help = new Integer[list.length];      // Helper array

        printArray(list);

        // Merge arrays of 1 elements
        MergeSort.merge(list, 0, 1, 2, help);

        printArray(list);
    }







DEMO: 14-sort/10-merge-sort/Demo2.java

How to sort arrays using the merge( ) method

An array of 1 element is sorted !!
By merging 2 arrays of 1 elements, we get a sorted portion of size = 2
By merging 2 (sorted) arrays of 2 elements, we get a sorted portion of size = 4
 
Here is how to sort an array of 4 elements:

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

        Integer[] help = new Integer[list.length];      // Helper array

        printArray(list);

        // Merge arrays of 1 elements
        MergeSort.merge(list, 0, 1, 2, help);
        MergeSort.merge(list, 2, 3, 4, help);

        // Merge arrays of 2 elements
        MergeSort.merge(list, 0, 2, 4, help);

        printArray(list);
    }



DEMO: 14-sort/10-merge-sort/Demo3.java (and so on - see Demo4.java)