Review:   the merge sort algorithm

Review:   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
   
     /* ------------------------------------------------
        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 !
 } 

Synopsis of the merge sort algorithm

Summary of the 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 n,  T[] H)
 {
     if ( n <= 1 )          // A[s]..A[e] has 0 or 1 element
         return;            // No need to sort an array of 1 element...

    
   
     /* ------------------------------------------------
        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, n/2, H);

     MergeSort.sort(A, n/2, H);

     // Merge both sorted arrays
     merge(A, n,  H); // Running time = 2n (see: )
 } 

Recurrence relation of the running time of the merge sort algorithm

Suppose that the running time of MergeSort.sort(A, n, H) is T(n):

 // 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 n,  T[] H)
 {
     if ( n <= 1 )          // A[s]..A[e] has 0 or 1 element
         return;            // No need to sort an array of 1 element...
                            // Running time = 0 (no comparison performed)
    
   
     /* ------------------------------------------------
        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, n/2, H);   // Running time = T(n/2)

     MergeSort.sort(A, n/2, H);   // Running time = T(n/2)

     // Merge both sorted arrays
     merge(A, n,  H); // Running time = 2n (see: )
 } 

T(n) = 0                            for n <= 1
T(n) = 2 T(n/2) + 2n        for n >= 2

Solving the recurrence relation of the running time of the merge sort algorithm

  • The recurrence relation that represents the running time of the Merge Sort algorithm:

     T(n) = # primitive operations performed by Merge Sort for input size n 
    
     T(n) = 2T(n/2) + 2n              for n >= 2   
     T(1) = 0
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

Solving the recurrence relation of the running time of the merge sort algorithm

  • We use the telescoping technique to solve T(n):

     T(n) = # primitive operations performed by Merge Sort for input size n 
    
     T(n) = 2T(n/2) + 2n              for n >= 2
     T(1) = 0
    
     T(n) = 2T(n/2) + 2n              // T(n/2) = 2T(n/4) + 2(n/2)
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

Solving the recurrence relation of the running time of the merge sort algorithm

  • Substitute T(n/2):

     T(n) = # primitive operations performed by Merge Sort for input size n 
    
     T(n) = 2T(n/2) + 2n              for n >= 2
     T(1) = 0   
    
     T(n) = 2T(n/2) + 2n              // T(n/2) = 2T(n/4) + 2(n/2)
     T(n) = 2( 2T(n/4) + 2(n/2) ) + 2n
          = 22T(n/4)   + 4n/2 + 2n
          = 22T(n/4)   + 2n + 2n
       
    
    
    
    
    
    
    
    
    
    
    
    
    

Solving the recurrence relation of the running time of the merge sort algorithm

  • Use formula for T(n) to obtain a formula for T(n/4)

     T(n) = # primitive operations performed by Merge Sort for input size n 
    
     T(n) = 2T(n/2) + 2n              for n >= 2
     T(1) = 0   
    
     T(n) = 2T(n/2) + 2n              // T(n/2) = 2T(n/4) + 2(n/2)
     T(n) = 2( 2T(n/4) + 2(n/2) ) + 2n
          = 22T(n/4)   + 4n/2 + 2n
          = 22T(n/4)   + 2n + 2n      // T(n/4) = 2T(n/8) + 2(n/4)
       
    
    
    
    
    
    
    
    
    
    
    
    
    

Solving the recurrence relation of the running time of the merge sort algorithm

  • Substitute T(n/4):

     T(n) = # primitive operations performed by Merge Sort for input size n 
    
     T(n) = 2T(n/2) + 2n              for n >= 2
     T(1) = 0 
    
     T(n) = 2T(n/2) + 2n              // T(n/2) = 2T(n/4) + 2(n/2)
     T(n) = 2( 2T(n/4) + 2(n/2) ) + 2n
          = 22T(n/4)   + 4n/2 + 2n
          = 22T(n/4)   + 2n + 2n      // T(n/4) = 2T(n/8) + 2(n/4)
    
     T(n) = 22( 2T(n/8) + 2(n/4) ) + 2n + 2n
          = 23T(n/8)    + 8(n/4)   + 2n + 2n
          = 23T(n/8)    + 2n + 2n + 2n
    
    
    
    
    
    
    
    
    
    

Solving the recurrence relation of the running time of the merge sort algorithm

  • Keep on substituting until we reach the base case:

     T(n) = # primitive operations performed by Merge Sort for input size n 
    
     T(n) = 2T(n/2) + 2n              for n >= 2
     T(1) = 0 
    
     T(n) = 2T(n/2) + 2n              // T(n/2) = 2T(n/4) + 2(n/2)
     T(n) = 2( 2T(n/4) + 2(n/2) ) + 2n
          = 22T(n/4)   + 4n/2 + 2n
          = 22T(n/4)   + 2n + 2n      // T(n/4) = 2T(n/8) + 2(n/4)
    
     T(n) = 22( 2T(n/8) + 2(n/4) ) + 2n + 2n
          = 23T(n/8)    + 8(n/4)   + 2n + 2n
          = 23T(n/8)    + 2n + 2n + 2n
     ...
    
     T(n) = 2kT(n/(2k)=1)    + 2n + 2n + 2n + 2n + ... + 2n
                               <-------------------------->
                                       k terms
          = 2k * 0 + 2kn = 2kn
    
    
    
    

Solving the recurrence relation of the running time of the merge sort algorithm

  • Solve k (= log(n)):

     T(n) = # primitive operations performed by Merge Sort for input size n 
    
     T(n) = 2T(n/2) + 2n              for n >= 2
     T(1) = 0  
    
     T(n) = 2T(n/2) + 2n              // T(n/2) = 2T(n/4) + 2(n/2)
     T(n) = 2( 2T(n/4) + 2(n/2) ) + 2n
          = 22T(n/4)   + 4n/2 + 2n
          = 22T(n/4)   + 2n + 2n      // T(n/4) = 2T(n/8) + 2(n/4)
    
     T(n) = 22( 2T(n/8) + 2(n/4) ) + 2n + 2n
          = 23T(n/8)    + 8(n/4)   + 2n + 2n
          = 23T(n/8)    + 2n + 2n + 2n
     ...
    
     T(n) = 2kT(n/(2k)=1)    + 2n + 2n + 2n + 2n + ... + 2n
                               <-------------------------->
                                       k terms
          = 2k * 0 + 2kn = 2kn
    
     n/(2k) = 1  <==>   2k = n   <==>  k = log(n)
    
    

Solving the recurrence relation of the running time of the merge sort algorithm

  • We find that the running time of the Merge Sort algorithm is O(nlog(n):

     T(n) = # primitive operations performed by Merge Sort for input size n 
    
     T(n) = 2T(n/2) + 2n              for n >= 2
     T(1) = 0 
    
     T(n) = 2T(n/2) + 2n              // T(n/2) = 2T(n/4) + 2(n/2)
     T(n) = 2( 2T(n/4) + 2(n/2) ) + 2n
          = 22T(n/4)   + 4n/2 + 2n
          = 22T(n/4)   + 2n + 2n      // T(n/4) = 2T(n/8) + 2(n/4)
    
     T(n) = 22( 2T(n/8) + 2(n/4) ) + 2n + 2n
          = 23T(n/8)    + 8(n/4)   + 2n + 2n
          = 23T(n/8)    + 2n + 2n + 2n
     ...
    
     T(n) = 2kT(n/(2k)=1)    + 2n + 2n + 2n + 2n + ... + 2n
                               <-------------------------->
                                       k terms
          = 2k * 0 + 2kn = 2kn
    
     n/(2k) = 1  <==>   2k = n   <==>  k = log(n)
     Running time T(n) = 2kn = 2nlog(n)