Merging 2 sorted arrays

Merging 2 sorted arrays:

  • You are given two sorted arrays:

      a = [2.5, 3.5, 6.4, 7.5]    
      b = [1.1, 8.9, 9.2] 

  • Design an efficient algorithm to combine (= merge) the 2 sorted arrays into one single sorted array


  • Dictatic note:

    • In this set of slides, I am presenting a naive merge algorithm that merge 2 independent arrays into a 3rd array

    • The Merge sort algorithm uses a merge algorithm that merge 2 portions of a (same) array

    • I will discuss this "specialized" merge algorithm later

Illustrating the merge algorithm

  • Use 2 indexes i and j to point to the current elements in each array:

           i=0                         j=0
            |                           |
            V                           V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]    
    
       Result [ ] 

  • The current elements are the smallest value in each of the 2 arrays

  • The output result[] array is initially empty

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

Illustrating the merge algorithm

  • Current state:   compare a[i] and b[j]:

           i=0                         j=0
            |                           |
            V                           V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]    
    
       Result [ ] 

    Since b[j] < a[i], we copy b[j] to result[ ] and increment j:

           i=0                              j=1
            |                                |
            V                                V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]      
    
       Result [1.1] 

Illustrating the merge algorithm

  • Current state:   compare a[i] and b[j]:

           i=0                              j=1
            |                                |
            V                                V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]      
    
       Result [1.1]  

    Since a[i] < b[j], we copy a[i] to result[ ] and increment i:

                i=1                         j=1
                 |                           |
                 V                           V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]      
    
       Result [1.1, 2.5]  

Illustrating the merge algorithm

  • Current state:   compare a[i] and b[j]:

                i=1                         j=1
                 |                           |
                 V                           V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]      
    
       Result [1.1, 2.5]   

    Since a[i] < b[j], we copy a[i] to result[ ] and increment i:

                     i=2                    j=1
                      |                      |
                      V                      V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]      
    
       Result [1.1, 2.5, 3.5] 

Illustrating the merge algorithm

  • Current state:   compare a[i] and b[j]:

                     i=2                    j=1
                      |                      |
                      V                      V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]      
    
       Result [1.1, 2.5, 3.5]   

    Since a[i] < b[j], we copy a[i] to result[ ] and increment i:

                          i=3               j=1
                           |                 |
                           V                 V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]      
    
       Result [1.1, 2.5, 3.5, 6.4] 

Illustrating the merge algorithm

  • Current state:   compare a[i] and b[j]:

                          i=3               j=1
                           |                 |
                           V                 V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]      
    
       Result [1.1, 2.5, 3.5, 6.4]  

    Since a[i] < b[j], we copy a[i] to result[ ] and increment i:

                             i=5            j=1
                              |              |
                              V              V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]      
    
       Result [1.1, 2.5, 3.5, 6.4, 7.5] 

Illustrating the merge algorithm

  • Current state:   compare a[i] and b[j]:

                             i=5            j=1
                              |              |
                              V              V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]      
    
       Result [1.1, 2.5, 3.5, 6.4, 7.5]  

    Since array a is exhausted, we copy b[j] to result[ ] and increment j:

                             i=5                 j=2
                              |                   |
                              V                   V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]      
    
       Result [1.1, 2.5, 3.5, 6.4, 7.5, 8.9]  

Illustrating the merge algorithm

  • Current state:   compare a[i] and b[j]:

                             i=5                 j=2
                              |                   |
                              V                   V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]      
    
       Result [1.1, 2.5, 3.5, 6.4, 7.5, 8.9] 

    Since array a is exhausted, we copy b[j] to result[ ] and increment j:

                             i=5                    j=3
                              |                      |
                              V                      V
       a  [2.5, 3.5, 6.4, 7.5]     b  [1.1, 8.9, 9.2]      
    
       Result [1.1, 2.5, 3.5, 6.4, 7.5, 8.9, 9.2]  

Both arrays are exhausted and the merge algorithm terminates !

The merge algorithm to merge 2 sorted arrays

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

    // merge(result, a, b): merge 2 sorted arrays a and b into array result

    public static <T> void merge(T[] result, T[] a, T[] b)      
    {
        int i = 0, j = 0, k = 0;  // Indexes for arrays: a[i], b[j], result[k]

        while ( i < a.length || j < b.length )
        {
       	    if ( i < a.length && j < b.length )
	    {  // Both array have elements

                if ( a[i] < b[j] )
	            result[k++] = a[i++];
                else
                    result[k++] = b[j++];
	    }
            else if ( i == a.length )     // a is empty
                result[k++] = b[j++];
	    else if ( j == b.length )     // b is empty
                result[k++] = a[i++];
        }
    } 

We parameterize the data type of the arrays... however...

The merge algorithm to merge 2 sorted arrays

We must limit the data type to a subclass of Comparable<T> because the objects must be comparable:

    // merge(result, a, b): merge 2 sorted arrays a and b into array result

    public static <T extends Comparable<T>> void merge(T[] result, T[] a, T[] b)
    {
        int i = 0, j = 0, k = 0;  // Indexes for arrays: a[i], b[j], result[k]

        while ( i < a.length || j < b.length )
        {
       	    if ( i < a.length && j < b.length )
	    {  // Both array have elements

                if ( a[i] < b[j] )
	            result[k++] = a[i++];
                else
                    result[k++] = b[j++];
	    }
            else if ( i == a.length )     // a is empty
                result[k++] = b[j++];
	    else if ( j == b.length )     // b is empty
                result[k++] = a[i++];
        }
    } 

We now proceed to write the merge algorithm.

The merge algorithm to merge 2 sorted arrays

We first define and initialize the indexes that points to the elements in the (3) arrays:

    // merge(result, a, b): merge 2 sorted arrays a and b into array result

    public static <T extends Comparable<T>> void merge(T[] result, T[] a, T[] b)
    {
        int i = 0, j = 0, k = 0;  // Indexes for arrays: a[i], b[j], result[k]

        while ( i < a.length || j < b.length )
        {
       	    if ( i < a.length && j < b.length )
	    {  // Both array have elements

                if ( a[i] < b[j] )
	            result[k++] = a[i++];
                else
                    result[k++] = b[j++];
	    }
            else if ( i == a.length )     // a is empty
                result[k++] = b[j++];
	    else if ( j == b.length )     // b is empty
                result[k++] = a[i++];
        }
    } 

The merge algorithm to merge 2 sorted arrays

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

    // merge(result, a, b): merge 2 sorted arrays a and b into array result

    public static <T extends Comparable<T>> void merge(T[] result, T[] a, T[] b)
    {
        int i = 0, j = 0, k = 0;  // Indexes for arrays: a[i], b[j], result[k]

        while ( i < a.length || j < b.length )
        {
       	    if ( i < a.length && j < b.length )
	    {  // Both array have elements

                if ( a[i] < b[j] )
	            result[k++] = a[i++];
                else
                    result[k++] = b[j++];
	    }
            else if ( i == a.length )     // a is empty
                result[k++] = b[j++];
	    else if ( j == b.length )     // b is empty
                result[k++] = a[i++];
        }
    } 

The merge algorithm to merge 2 sorted arrays

We must handle 3 cases:

    // merge(result, a, b): merge 2 sorted arrays a and b into array result

    public static <T extends Comparable<T>> void merge(T[] result, T[] a, T[] b)
    {
        int i = 0, j = 0, k = 0;  // Indexes for arrays: a[i], b[j], result[k]

        while ( i < a.length || j < b.length )
        {

	
	     case 1:  both a and b have unprocessed elements
     
	     case 2:  array a is exhausted
              
             case 3:  array b is exhausted
	 
           
            
	    
              
        }
    } 

The merge algorithm to merge 2 sorted arrays

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

    // merge(result, a, b): merge 2 sorted arrays a and b into array result

    public static <T extends Comparable<T>> void merge(T[] result, T[] a, T[] b)
    {
        int i = 0, j = 0, k = 0;  // Indexes for arrays: a[i], b[j], result[k]

        while ( i < a.length || j < b.length )
        {
       	    if ( i < a.length && j < b.length )
	    {  // Both arrays have unprocessed elements

                if ( a[i] < b[j] )
	            result[k++] = a[i++];
                else
                    result[k++] = b[j++];
	    }
            else if ( i == a.length )     // a is empty
                result[k++] = b[j++];
	    else if ( j == b.length )     // b is empty
                result[k++] = a[i++];
        }
    } 

The merge algorithm to merge 2 sorted arrays

Case 1:   if a[i] < b[j], we copy a[i] over to result[k] (and increment   i   and  k):

    // merge(result, a, b): merge 2 sorted arrays a and b into array result

    public static <T extends Comparable<T>> void merge(T[] result, T[] a, T[] b)
    {
        int i = 0, j = 0, k = 0;  // Indexes for arrays: a[i], b[j], result[k]

        while ( i < a.length || j < b.length )
        {
       	    if ( i < a.length && j < b.length )
	    {  // Both arrays have unprocessed elements
                if ( a[i] < b[j] ) {
	            result[k] = a[i];
                    i = i + 1;
                    k = k + 1;          
                }
	    }
            else if ( i == a.length )     // a is empty
                result[k++] = b[j++];
	    else if ( j == b.length )     // b is empty
                result[k++] = a[i++];
        }
    } 

The merge algorithm to merge 2 sorted arrays

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

    // merge(result, a, b): merge 2 sorted arrays a and b into array result

    public static <T extends Comparable<T>> void merge(T[] result, T[] a, T[] b)
    {
        int i = 0, j = 0, k = 0;  // Indexes for arrays: a[i], b[j], result[k]

        while ( i < a.length || j < b.length )
        {
       	    if ( i < a.length && j < b.length )
	    {  // Both arrays have unprocessed elements
                if ( a[i] < b[j] ) {
	            result[k] = a[i];  // Use i and k
                    i = i + 1;         // After using i, increment i by 1
                    k = k + 1;         // After using k, increment k by 1
                }
	    }
            else if ( i == a.length )     // a is empty
                result[k++] = b[j++];
	    else if ( j == b.length )     // b is empty
                result[k++] = a[i++];
        }
    } 

The merge algorithm to merge 2 sorted arrays

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

    // merge(result, a, b): merge 2 sorted arrays a and b into array result

    public static <T extends Comparable<T>> void merge(T[] result, T[] a, T[] b)
    {
        int i = 0, j = 0, k = 0;  // Indexes for arrays: a[i], b[j], result[k]

        while ( i < a.length || j < b.length )
        {
       	    if ( i < a.length && j < b.length )
	    {  // Both arrays have unprocessed elements
                if ( a[i] < b[j] )
	            result[k++] = a[i++];
                else
                    result[k++] = b[j++];

	    }
            else if ( i == a.length )     // a is empty
                result[k++] = b[j++];
	    else if ( j == b.length )     // b is empty
                result[k++] = a[i++];
        }
    } 

The merge algorithm to merge 2 sorted arrays

Case 1:   if a[i] ≥ b[j], we copy b[j] over to result[k] (and increment  j  and  k):

    // merge(result, a, b): merge 2 sorted arrays a and b into array result

    public static <T extends Comparable<T>> void merge(T[] result, T[] a, T[] b)
    {
        int i = 0, j = 0, k = 0;  // Indexes for arrays: a[i], b[j], result[k]

        while ( i < a.length || j < b.length )
        {
       	    if ( i < a.length && j < b.length )
	    {  // Both arrays have unprocessed elements
                if ( a[i] < b[j] )
	            result[k++] = a[i++];
                else
                    result[k++] = b[j++];

	    }
            else if ( i == a.length )     // a is empty
                result[k++] = b[j++];
	    else if ( j == b.length )     // b is empty
                result[k++] = a[i++];
        }
    } 

The merge algorithm to merge 2 sorted arrays

Case 2:   if array a is exhausted, we copy b[j] over to result[k] (and increment  j  and  k):

    // merge(result, a, b): merge 2 sorted arrays a and b into array result

    public static <T extends Comparable<T>> void merge(T[] result, T[] a, T[] b)
    {
        int i = 0, j = 0, k = 0;  // Indexes for arrays: a[i], b[j], result[k]

        while ( i < a.length || j < b.length )
        {
       	    if ( i < a.length && j < b.length )
	    {  // Both arrays have unprocessed elements
                if ( a[i] < b[j] )
	            result[k++] = a[i++];
                else
                    result[k++] = b[j++];

	    }
            else if ( i == a.length )     // a is exhausted (empty)
                result[k++] = b[j++];
	    else if ( j == b.length )     // b is empty
                result[k++] = a[i++];
        }
    } 

The merge algorithm to merge 2 sorted arrays

Case 3:   if array b is exhausted, we copy a[i] over to result[k] (and increment  i  and  k):

    // merge(result, a, b): merge 2 sorted arrays a and b into array result

    public static <T extends Comparable<T>> void merge(T[] result, T[] a, T[] b)
    {
        int i = 0, j = 0, k = 0;  // Indexes for arrays: a[i], b[j], result[k]

        while ( i < a.length || j < b.length )
        {
       	    if ( i < a.length && j < b.length )
	    {  // Both arrays have unprocessed elements
                if ( a[i] < b[j] )
	            result[k++] = a[i++];
                else
                    result[k++] = b[j++];

	    }
            else if ( i == a.length )     // a is exhausted (empty)
                result[k++] = b[j++];
	    else if ( j == b.length )     // b is exhausted (empty)
                result[k++] = a[i++];
        }
    } 

DEMO: 14-sort/10-merge-sort/MergeSort.java --- see next slides for test programs

Test program 1

Test program that merges 2 arrays of (sorted) Integers:

    public static void main(String[] args)
    {
        Integer[] list1 = {1, 3, 5, 7, 8};  // Sorted
        Integer[] list2 = {2, 4, 8, 9};     // Sorted

        Integer[] r = new double[list1.length + list2.length];
        
        for(int i = 0; i < list1.length; i++)
            System.out.print(list1[i] + " ");
        System.out.println();
        
        for(int i = 0; i < list2.length; i++)
            System.out.print(list2[i] + " ");
        System.out.println();
        
        MergeSort.<Integer>merge(r, list1, list2);

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

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

Test program 2

Test program that merges 2 arrays of (sorted) Strings:

    public static void main(String[] args)
    {
        String[] list1 = {"abc", "klm", "pqr"};
        String[] list2 = {"def", "ghi", "xyz"};

        String[] r = new String[list1.length + list2.length];
        
        for(int i = 0; i < list1.length; i++)
            System.out.print(list1[i] + " ");
        System.out.println();
        
        for(int i = 0; i < list2.length; i++)
            System.out.print(list2[i] + " ");
        System.out.println();
        
        MergeSort.<String>merge(r, list1, list2);

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

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