Review: the parttion algorithm of the Quick Sort algorithm

  • Input: a sub array A[s] .. A[e-1]:

       |   |   |   |   |   |   |   |   |   |   |   |   |   |
       | 4 |   | 5 |   | 6 |   | 3 |   | 2 |   | 1 |   | 7 |
       +---+   +---+   +---+   +---+   +---+   +---+   +---+  
         s      s+1     ...                             e-1    e 
    

  • We select A[s] as pivot:

     pivot = 4
       |   |   |   |   |   |   |   |   |   |   |   |   |   |
       | 4 |   | 5 |   | 6 |   | 3 |   | 2 |   | 1 |   | 7 |
       +---+   +---+   +---+   +---+   +---+   +---+   +---+   
    

  • Then we partition the values into 2 subgroups: (1) values < pivot and (2) values ≥ pivot:

     
       |   |   |   |   |   |   |   |   |   |   |   |   |   |
       | 3 |   | 2 |   | 1 |   | 4 |   | 5 |   | 6 |   | 7 |
       +---+   +---+   +---+   +---+   +---+   +---+   +---+   
       <------ group 1 ---->  pivotLoc <------ group 2 ----->
    

  • We must return the pivotLoc

A simplified partition algorithm

  • The partition( ) method discussed in Liang's text book is a bit complicated

  • I have developed and will present a simpler (but less efficient) partition( ) method

  • A partitioning algorithm maintains 2 half arrays: a low half and a high half:

                  (1) low half                 (2) high half
     A[] =  [ P  L  L  L  L  L  ?  ?  ?  ?  ?  H  H  H  H  H ]
              ^  <----------->  ^           ^  <----------->
    	  |      < P        |           |      >= P
            pivot              low         high
    

  • The simplified partitioning algorithm only compares the pivot against A[high]:

                  (1) low half                 (2) high half
     A[] =  [ P  L  L  L  L  L  ?  ?  ?  ?  X  H  H  H  H  H ]
              ^  <----------->  ^           ^  <----------->
    	  |      < P        |           |      >= P
            pivot              low         high
              |                             |
              +-----------------------------+
                compares: pivot <==> A[high] 
    

A simplified partition algorithm

  • (1) If A[high] (X) >= pivot:

                  (1) low half                 (2) high half
     A[] =  [ P  L  L  L  L  L  ?  ?  ?  ?  X  H  H  H  H  H ]
              ^  <----------->  ^           ^  <----------->
    	  |      < P        |           |      >= P
            pivot              low         high   
              |                             |
              +-----------------------------+   A[high] (X) >= pivot
    

    Then A[high] belongs to the high partition and we decrement high:

                  (1) low half                 (2) high half
     A[] =  [ P  L  L  L  L  L  ?  ?  ?  ?  X  H  H  H  H  H ]
              ^  <----------->  ^        ^  <-------------->
    	  |      < P        |        |         >= P
            pivot              low      high (high--)
              |                          |
              +--------------------------+      A[high] (X) >= pivot
    

A simplified partition algorithm

  • (2) If A[high] (X) < pivot:

                  (1) low half                 (2) high half
     A[] =  [ P  L  L  L  L  L  Y  ?  ?  ?  X  H  H  H  H  H ]
              ^  <----------->  ^           ^  <----------->
    	  |      < P        |           |      >= P
            pivot              low         high   
              |                             |
              +-----------------------------+   A[high] (X) <  pivot
    

    Then A[high] belongs to the low partition: we swap(X,Y) and then increment low:

                  (1) low half                 (2) high half
     A[] =  [ P  L  L  L  L  L  X  ?  ?  ?  Y  H  H  H  H  H ]
              ^  <-------------->  ^        ^  <------------->
    	  |      < P           |        |      >= P
            pivot                 low      high   
              |                             |
              +-----------------------------+   A[high] (X) <  pivot
    

Example of the simplified partition( ) algorithm

  • Sample input:

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   4,   1,   7,   2,   9,   3,   6]
    
    
    
    
    
    
    
      
    

     

Example of the simplified partition( ) algorithm

  • Initialize the pivot:

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   4,   1,   7,   2,   9,   3,   6]
    
    
    
    
    
    
    
       pivot = 5 (= A[s])
    

     

Example of the simplified partition( ) algorithm

  • Initialize the low index and the high index:

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   4,   1,   7,   2,   9,   3,   6]
                          ^                             ^
                          |                             |
                       low = s+1                    high = e-1
    
    
    
    
       pivot = 5 (= A[s])
    

     

Example of the simplified partition( ) algorithm

  • Compare A[high] against the pivot:   6 >= 5

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   4,   1,   7,   2,   9,   3,   6]
                          ^                             ^
                          |                             |
                       low = s+1                    high = e-1
    
    
    
    
       pivot = 5 (= A[s])
    

    A[high] (= 6) belongs to the high portion, theorefore:   high--

Example of the simplified partition( ) algorithm

  • Result:

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   4,   1,   7,   2,   9,   3,   6]
                          ^                        ^
                          |                        |
                       low = s+1                  high
    
    
    
    
       pivot = 5 (= A[s])
    

     

Example of the simplified partition( ) algorithm

  • Compare A[high] against the pivot:   3 < 5

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   4,   1,   7,   2,   9,   3,   6]
                          ^                        ^
                          |                        |
                       low = s+1                  high
    
    
    
    
       pivot = 5 (= A[s])
    

    A[high] (= 3) belongs to the low portion, theorefore: swap(4,3) and low++

Example of the simplified partition( ) algorithm

  • Result:

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   3,   1,   7,   2,   9,   4,   6]
                               ^                   ^
                               |                   |
                              low                 high
    
    
    
    
       pivot = 5 (= A[s])
    

     

Example of the simplified partition( ) algorithm

  • Compare A[high] against the pivot:   4 < 5

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   3,   1,   7,   2,   9,   4,   6]
                               ^                   ^
                               |                   |
                              low                 high
    
    
    
    
       pivot = 5 (= A[s])
    

    A[high] (= 4) belongs to the low portion, theorefore: swap(1,4) and low++

Example of the simplified partition( ) algorithm

  • Result:

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   3,   4,   7,   2,   9,   1,   6]
                                    ^              ^
                                    |              |
                                   low            high
    
    
    
    
       pivot = 5 (= A[s])
    

     

Example of the simplified partition( ) algorithm

  • Compare A[high] against the pivot:   1 < 5

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   3,   4,   7,   2,   9,   1,   6]
                                    ^              ^
                                    |              |
                                   low            high
    
    
    
    
       pivot = 5 (= A[s])
    

    A[high] (= 1) belongs to the low portion, theorefore:   swap(7,1) and low++

Example of the simplified partition( ) algorithm

  • Result:

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   3,   4,   1,   2,   9,   7,   6]
                                         ^         ^
                                         |         |
                                        low       high
    
    
    
    
       pivot = 5 (= A[s])
    

     

Example of the simplified partition( ) algorithm

  • Compare A[high] against the pivot:   7 >= 5

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   3,   4,   1,   2,   9,   7,   6]
                                         ^         ^
                                         |         |
                                        low       high
    
    
    
    
       pivot = 5 (= A[s])
    

    A[high] (= 7) belongs to the high portion, theorefore:   high--

Example of the simplified partition( ) algorithm

  • Result:

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   3,   4,   1,   2,   9,   7,   6]
                                         ^    ^
                                         |    |
                                        low  high
    
    
    
    
       pivot = 5 (= A[s])
    

     

Example of the simplified partition( ) algorithm

  • Compare A[high] against the pivot:   2 >= 5

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   3,   4,   1,   2,   9,   7,   6]
                                         ^    
                                         |   
                                        low  
                                         ^
                                         |
                                        high
    
       pivot = 5 (= A[s])
    

    A[high] (= 2) belongs to the low portion, theorefore:   swap(2,2) and low++

Example of the simplified partition( ) algorithm

  • Result:   since low > high , the loop exits !!!

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   3,   4,   1,   2,   9,   7,   6]
                                         ^    ^
                                         |    |
                                        high low  
                                        
                                        
                                       
    
       pivot = 5 (= A[s])
    

    There is one more thing that we need to do: put the pivot in its correct location

Example of the simplified partition( ) algorithm

  • To put the pivot in its correct location, we do exch(A, s, high):

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [5,   3,   4,   1,   2,   9,   7,   6]
                     ^                   ^    ^
                     |                   |    |
                     |                  high low  
                     |                   |
                     +-------------------+ 
                             swap        
    
       pivot = 5 (= A[s])
    

     

Example of the simplified partition( ) algorithm

  • Final result:

     partition(A, s, e):
    
       index --->    s   s+1                           e-1    e
    
       A[ ]         [2,   3,   4,   1,   5,   9,   7,   6]
                                         ^    ^
                                         |    |
                                        high low  
                                         |
                                   pivot position  
                                
    
       pivot = 5 (= A[s])
    

    partition( ) must return high (= the pivot position)

The simplified partition( ) algorithm

The simplified partition( ) algorithm:

    // partition(A, s, e): partition A[s]..A[e-1] using pivot A[s]

    public static <T extends Comparable<T>> int partition(T[] A, int s, int e)
    {
        T pivot = A[s];

        int low = s+1, high = e-1;

        while (low <= high)
        {
            if ( A[high].compareTo(pivot) >= 0 )
            {
                high--;
            }
            else
            {
                exch(A, low, high);
                low++;
            }

        }

        exch(A, s, high);

        return high;
    }

The simplified partition( ) algorithm

Select pivot = A[s]:

    // partition(A, s, e): partition A[s]..A[e-1] using pivot A[s]

    public static <T extends Comparable<T>> int partition(T[] A, int s, int e)
    {
        T pivot = A[s];

        int low = s+1, high = e-1;

        while (low <= high)
        {
            if ( A[high].compareTo(pivot) >= 0 )
            {
                high--;
            }
            else
            {
                exch(A, low, high);
                low++;
            }

        }

        exch(A, s, high);

        return high;
    }

The simplified partition( ) algorithm

Initialize the low and high indixes:

    // partition(A, s, e): partition A[s]..A[e-1] using pivot A[s]

    public static <T extends Comparable<T>> int partition(T[] A, int s, int e)
    {
        T pivot = A[s];

        int low = s+1, high = e-1;

        while (low <= high)
        {
            if ( A[high].compareTo(pivot) >= 0 )
            {
                high--;
            }
            else
            {
                exch(A, low, high);
                low++;
            }

        }

        exch(A, s, high);

        return high;
    }

The simplified partition( ) algorithm

As long as low ≤ high (i.e., have not crossed), we compare A[high] against the pivot:

    // partition(A, s, e): partition A[s]..A[e-1] using pivot A[s]

    public static <T extends Comparable<T>> int partition(T[] A, int s, int e)
    {
        T pivot = A[s];

        int low = s+1, high = e-1;

        while (low <= high)
        {
            if ( A[high].compareTo(pivot) >= 0 )
            {
                high--;
            }
            else
            {
                exch(A, low, high);
                low++;
            }

        }

        exch(A, s, high);

        return high;
    }

The simplified partition( ) algorithm

If A[high] >= pivot, then A[high] belongs to the high portion and we decrement high :

    // partition(A, s, e): partition A[s]..A[e-1] using pivot A[s]

    public static <T extends Comparable<T>> int partition(T[] A, int s, int e)
    {
        T pivot = A[s];

        int low = s+1, high = e-1;

        while (low <= high)
        {
            if ( A[high].compareTo(pivot) >= 0 )
            {
                high--;
            }
            else
            {
                exch(A, low, high);
                low++;
            }

        }

        exch(A, s, high);

        return high;
    }

The simplified partition( ) algorithm

Otherwise A[high] belongs to the low portion and we (1) swap(A[low],A[high]) and (2) increment low :

    // partition(A, s, e): partition A[s]..A[e-1] using pivot A[s]

    public static <T extends Comparable<T>> int partition(T[] A, int s, int e)
    {
        T pivot = A[s];

        int low = s+1, high = e-1;

        while (low <= high)
        {
            if ( A[high].compareTo(pivot) >= 0 )
            {
                high--;
            }
            else
            {
                exch(A, low, high);
                low++;
            }

        }

        exch(A, s, high);

        return high;
    }

The simplified partition( ) algorithm

Finally, we (1) swap(pivot,A[high]) and (2) return high :

    // partition(A, s, e): partition A[s]..A[e-1] using pivot A[s]

    public static <T extends Comparable<T>> int partition(T[] A, int s, int e)
    {
        T pivot = A[s];

        int low = s+1, high = e-1;

        while (low <= high)
        {
            if ( A[high].compareTo(pivot) >= 0 )
            {
                high--;
            }
            else
            {
                exch(A, low, high);
                low++;
            }

        }

        exch(A, s, high); // A[s] = pivot

        return high;
    }

Demo program

    // Sort integers

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

        System.out.println("Input  = " + Arrays.toString(A) + "\n");
        QuickSort.sort(A, 0, A.length);
        System.out.println("Output = " + Arrays.toString(A) + "\n");
    }


// Sort strings public static void main(String[] args) { String[] A = { "klm", "xyz", "abc", "uvw", "qrs", "fgh", "bcd", "mno"}; System.out.println("Input = " + Arrays.toString(A) + "\n"); QuickSort.sort(A, 0, A.length); System.out.println("Output = " + Arrays.toString(A) + "\n"); }

DEMO: demo/14-sort/14-quick-sort/Demo.java + Demo2.java

The partition algorithm in Liang's text book

The partition( ) method discussed in Liang's text book is as follows:

  1. Initialize: (1) pivot = A[s], (2) low = s+1 and (3) high = s-1

     A[] =  [ 5,   3,   4,   7,   8,   1,   2,   6,   9 ]
                   ^                                  ^
                   |				  |
    	      low                                high
    

  2. Starting at index low, find forward the first element that is > pivot:

     A[] =  [ 5,   3,   4,   7,   8,   1,   2,   6,   9 ]
                             ^                        ^
                             |			  |
    	                low                      high
    

  3. Starting at index high, find backward the first element that is < pivot:

     A[] =  [ 5,   3,   4,   7,   8,   1,   2,   6,   9 ]
                             ^              ^
                             |              |
    	                low            high
    

  4. Swap A[low] <--> A[high] (1 swap will move 2 values in their correct places)

The partition algorithm in Liang's text book

public static int partition(int[] list, int first, int last) 
{
   int pivot = list[first]; // Choose the first element as the pivot
   int low   = first + 1;   // Index for forward search
   int high =  last  - 1;   // Index for backward search

   while (low < high) 
   {
      // Search forward from left
      while (low <= high && list[low] <= pivot)
         low++;

      // Search backward from right
      while (low <= high && list[high] > pivot)
         high--;

      // Swap two elements in the list
      if (low < high) 
      {
         int temp = list[high];
         list[high] = list[low];
         list[low] = temp;
      } 
   }

   // Adjust high to find the border
   while (high > first && list[high] >= pivot)
      high--;

   // Swap pivot with list[high]
   if (pivot > list[high]) 
   {
      list[first] = list[high];
      list[high] = pivot;
      return high;
   }
   else 
   {
      return first;     // Pivot was the smallest element...
   }
}

Additional properties of Quick Sort algorithm

  • Recall: In-place

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

    The Quick Sort algorithm is 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 Quick Sort algorithm is not stable

    Example: partition( ) will swap(9,4) and re-arange the ordering of the 4's

      A[] = [  5   1   9   ..  ..  ..  ..  4  4  8 ]
                       ^                      ^
                       |                      |
                      low                    high
    
      A[] = [  5   1   4   ..  ..  ..  ..  4  9  8 ]