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) + cn              for n >= 2     (c = 2)
     T(n) = 0                         for n  = 1
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

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) + cn              for n >= 2     (c = 2)
     T(n) = 0                         for n  = 1
    
     T(n) = 2T(n/2) + cn              // T(n/2) = 2T(n/4) + c(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) + cn              for n >= 2     (c = 2)
     T(n) = 0                         for n  = 1
    
     T(n) = 2T(n/2) + cn              // T(n/2) = 2T(n/4) + c(n/2)
     T(n) = 2( 2T(n/4) + c(n/2) ) + cn
          = 22T(n/4)   + 2cn/2 + cn
          = 22T(n/4)   + cn + cn
       
    
    
    
    
    
    
    
    
    
    
    
    
    

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) + cn              for n >= 2     (c = 2)
     T(n) = 0                         for n  = 1
    
     T(n) = 2T(n/2) + cn              // T(n/2) = 2T(n/4) + c(n/2)
     T(n) = 2( 2T(n/4) + c(n/2) ) + cn
          = 22T(n/4)   + 2cn/2 + cn
          = 22T(n/4)   + cn + cn      // T(n/4) = 2T(n/8) + c(n/8)
       
    
    
    
    
    
    
    
    
    
    
    
    
    

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) + cn              for n >= 2     (c = 2)
     T(n) = 0                         for n  = 1
    
     T(n) = 2T(n/2) + cn              // T(n/2) = 2T(n/4) + c(n/2)
     T(n) = 2( 2T(n/4) + c(n/2) ) + cn
          = 22T(n/4)   + 2cn/2 + cn
          = 22T(n/4)   + cn + cn      // T(n/4) = 2T(n/8) + c(n/8)
    
     T(n) = 22( 2T(n/8) + c(n/8) ) + cn + cn
          = 23T(n/8)    + 8cn/8    + cn + cn
          = 23T(n/8)    + cn + cn + cn
    
    
    
    
    
    
    
    
    
    

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) + cn              for n >= 2     (c = 2)
     T(n) = 0                         for n  = 1
    
     T(n) = 2T(n/2) + cn              // T(n/2) = 2T(n/4) + c(n/2)
     T(n) = 2( 2T(n/4) + c(n/2) ) + cn
          = 22T(n/4)   + 2cn/2 + cn
          = 22T(n/4)   + cn + cn      // T(n/4) = 2T(n/8) + c(n/8)
    
     T(n) = 22( 2T(n/8) + c(n/8) ) + cn + cn
          = 23T(n/8)    + 8cn/8    + cn + cn
          = 23T(n/8)    + cn + cn + cn
     ...
    
     T(n) = 2kT(n/(2k)=1)    + cn + cn + cn + cn + ... + cn
                               <-------------------------->
                                       k terms
          = 2k * 0 + k(cn) = kcn
    
    
    
    

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) + cn              for n >= 2     (c = 2)
     T(n) = 0                         for n  = 1
    
     T(n) = 2T(n/2) + cn              // T(n/2) = 2T(n/4) + c(n/2)
     T(n) = 2( 2T(n/4) + c(n/2) ) + cn
          = 22T(n/4)   + 2cn/2 + cn
          = 22T(n/4)   + cn + cn      // T(n/4) = 2T(n/8) + c(n/8)
    
     T(n) = 22( 2T(n/8) + c(n/8) ) + cn + cn
          = 23T(n/8)    + 8cn/8    + cn + cn
          = 23T(n/8)    + cn + cn + cn
     ...
    
     T(n) = 2kT(n/(2k)=1)    + cn + cn + cn + cn + ... + cn
                               <-------------------------->
                                       k terms
          = 2k * 0 + k(cn) = kcn
    
     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) + cn              for n >= 2     (c = 2)
     T(n) = 0                         for n  = 1
    
     T(n) = 2T(n/2) + cn              // T(n/2) = 2T(n/4) + c(n/2)
     T(n) = 2( 2T(n/4) + c(n/2) ) + cn
          = 22T(n/4)   + 2cn/2 + cn
          = 22T(n/4)   + cn + cn      // T(n/4) = 2T(n/8) + c(n/8)
    
     T(n) = 22( 2T(n/8) + c(n/8) ) + cn + cn
          = 23T(n/8)    + 8cn/8    + cn + cn
          = 23T(n/8)    + cn + cn + cn
     ...
    
     T(n) = 2kT(n/(2k)=1)    + cn + cn + cn + cn + ... + cn
                               <-------------------------->
                                       k terms
          = 2k * 0 + k(cn) = kcn
    
     n/(2k) = 1  <==>   2k = n   <==>  k = log(n)
     Running time = kcn = cnlog(n) = 2nlog(n)   or  O(nlog(n))