### The in-place quick-sort algorithm (what people use)

• Inefficiency in the simple to understand version of Quick Sort

• Recall: Merge Sort in pseudo code

 ``` QuickSort( double[] a ) { if ( a.length ≤ 1 ) return; // Don't need sorting Select a pivot; // It's usually the last elem in a[] Partition a[] in 2 halves: left[]: elements ≤ pivot right[]: elements > pivot; Sort left[]; Sort right[]; Concatenate: left[] pivot right[] } ```

• In the simple to understand version:

• we create two new arrays ( left[] and right[])

 The arrays left[] and right[] contains some portion of the original array

• and pass these array to another merge sort method

• More efficient way to pass a portion of an array

• There is a better way to pass a sub-array to sort it:

• Use the same array

 This will eliminate the copying of elements (save time !!)

• We specify the range of the elements that you want to sort by passing the range as parameter variables

Example:

 ``` sort( a, from, to ) means: sort ONLY the elements between from and to (not inclusive): a[from], a[from+1], ..... , a[to-1] ```

• In-place Quick Sort:

• The resulting Quick Sort Algorithm will use only one array (the input array)

• It is therefore called:

 In-place Quick Sort.

• Operation of the in-place Quick Sort Algorithm

• We use the same pseudo code:

 ``` QuickSort( double[] a ) { if ( a.length ≤ 1 ) return; // Don't need sorting Select a pivot; // It's usually the last elem in a[] Partition a[] in 2 halves: left[]: elements ≤ pivot right[]: elements > pivot; Sort left[]; Sort right[]; Concatenate: left[] pivot right[] } ```

• The only difference is:

 We must use the same array in every operation (That's what it means to be in-place)

• In Java:

 ``` /* ======================================================== Sort: a[iLeft] a[iLeft+1] .... a[iRight-1] ======================================================== */ void QuickSort( double[] a, int iLeft, int iRight ) { if ( iRight - iLeft ≤ 1 ) { // An empty array or an array of 1 element don't need sorting ! return; } k = partition( a, iLeft, iRight ); // Returns the pos. of pivit after partitioning QuickSort( a, iLeft, k ); // Sort left half of partitioned array QucikSort( a, k+1, iRight); // Sort right half of partitioned array // The catenate step is NOT necessary !!! // There is NO NEED to concatenate the 2 sorted arrays // because the sorting happened inside input array a !!! } ```

Note: the effect of the call partition(a, iLeft, iRight) is as follows call

• The call partition(a, iLeft, iRight) will partition the elements:

 ``` a[iLeft] a[iLeft+1] ..... a[iRight-1] ```

by picking the pivot = a[iRight-1] and then partition the elements into 2 halves:

 The left half contain only values that are ≤ pivot element The right half contain only values that are > pivot element

• Example 1: effect of the partition() call

• Example 2: partition() can use a portion of the array

• partition() must use the same array (i.e., the input array a[])

 This is called the in-place partition algorithm

• We will discuss the the in-place partition algorithm later

(It is the most complex part of the in-place Quick Sort algorithm)

More details on what the partition() method does can be found here: click here

I will first show you how the algorithm operates before discussing the in-place partitioning algorithm

• Operation of the in-place Quick Sort Algorithm

• I will now illustrate how the in-place Quick Sort Algorithm operates using an example....

• How the in-place Quick Sort Algorithm operates:

• Input array:

• To sort the array, we call: QuickSort(a, 0, 8) (notice that iRight = 8)

• QuickSort(a, 0, 8) will first partition the array:

Notice that:

 The method call partition(a, 0, 8) will change the input array !!!

When partition(a, 0, 8) is done, it will return the index 3 (that's the position of the pivot):

(Notice the input array is changed "in place")

• QuickSort(a, 0, 8) will then call QuickSort(a, 0, 3) to sort the left array:

• QuickSort(a, 0, 3) will first partition the array:

partition(a, 0, 3) will return the index 1 (that's the position of the pivot)

• QuickSort(a, 0, 3) will then call QuickSort(a, 0, 1) to sort the left array:

Since the array has one element, QuickSort(a, 0, 1) returns immediately

• QuickSort(a, 0, 3) will next call QuickSort(a, 2(= k+1), 3) to sort the right array:

Since the array has one element, QuickSort(a, 2, 3) also returns immediately

• We return back to QuickSort(a, 0, 3) and this method is now finally complete and return:

• We return to QuickSort(a, 0, 8), the input array will look like this:

And QuickSort(a, 0, 8) will call QuickSort(a, 4 (= k+1), 8) to sort the right array:

• This will recurse and sort the right array

(I will omit the picture.... too many to draw)

• Here is a print out of an actual execution of the in place Quick Sort algorithm using the input above:

 ```Before sort: [6.4, 2.5, 9.2, 3.5, 8.9, 1.1, 7.5, 4.2] **QuickSort(a, 0, 8)** Input: [6.4, 2.5, 9.2, 3.5, 8.9, 1.1, 7.5, 4.2] a[] = [6.4, 2.5, 9.2, 3.5, 8.9, 1.1, 7.5, 4.2] Pivot = 4.2 After partition: [1.1, 3.5, 2.5, 4.2, 9.2, 8.9, 6.4, 7.5] **QuickSort(a, 0, 3)** Input: [1.1, 3.5, 2.5] a[] = [1.1, 3.5, 2.5, 4.2, 9.2, 8.9, 6.4, 7.5] Pivot = 2.5 After partition: [1.1, 2.5, 3.5, 4.2, 9.2, 8.9, 6.4, 7.5] **QuickSort(a, 0, 1)** Input: [1.1] a[] = [1.1, 2.5, 3.5, 4.2, 9.2, 8.9, 6.4, 7.5] Done. (**QuickSort(a, 0, 1)**) **QuickSort(a, 2, 3)** Input: [3.5] a[] = [1.1, 2.5, 3.5, 4.2, 9.2, 8.9, 6.4, 7.5] Done. (**QuickSort(a, 2, 3)**) [1.1] [3.5] Concatented pieces = [1.1, 2.5, 3.5] a[] = [1.1, 2.5, 3.5, 4.2, 9.2, 8.9, 6.4, 7.5] Done. (**QuickSort(a, 0, 3)**) **QuickSort(a, 4, 8)** Input: [9.2, 8.9, 6.4, 7.5] a[] = [1.1, 2.5, 3.5, 4.2, 9.2, 8.9, 6.4, 7.5] Pivot = 7.5 After partition: [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] Input: [6.4] a[] = [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] Done. Input: [8.9, 9.2] a[] = [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] Pivot = 9.2 After partition: [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] Input: [8.9] a[] = [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] Done. Input: [] a[] = [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] Done. [8.9] [] Concatented pieces = [8.9, 9.2] a[] = [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] [6.4] [8.9, 9.2] Concatented pieces = [6.4, 7.5, 8.9, 9.2] a[] = [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] [1.1, 2.5, 3.5] [6.4, 7.5, 8.9, 9.2] Concatented pieces = [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] a[] = [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] After sort: [1.1, 2.5, 3.5, 4.2, 6.4, 7.5, 8.9, 9.2] ```

• The in-place partition algorithm

• Recall that the in-place Quick Sort algorithm:

 ``` /* ======================================================== Sort: a[iLeft] a[iLeft+1] .... a[iRight-1] ======================================================== */ void QuickSort( double[] a, int iLeft, int iRight ) { if ( iRight - iLeft ≤ 1 ) { // An empty array or an array of 1 element don't need sorting ! return; } k = partition( a, iLeft, iRight ); // Returns the pos. of pivit after partitioning QuickSort( a, iLeft, k ); // Sort left half of partitioned array QucikSort( a, k+1, iRight); // Sort right half of partitioned array } ```

must partition the array a[] "in-place"

Examples:

• Example 1:

Explanation:

• partition(a, 0, 8) will partition the array elements:

 ``` a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] ```

• First, we select the pivot = a[7] (= 4.2)

• Then the rest of the array elements are partitioned in 2 halves:

 ``` left half with values ≤ 4.2 ===> 2.5 3.5 1.1 right half with values > 4.2 ===> 6.4 9.2 8.9 7.5 ```

• Example 2:

Explanation:

• partition(a, 0, 4) will partition the array elements:

 ``` a[0] a[1] a[2] a[3] (3 = 4-1) ```

• First, we select the pivot = a[3] (= 3.5)

• Then the rest of the array elements are partitioned in 2 halves:

 ``` left half with values ≤ 3.5 ===> 2.5 right half with values > 3.5 ===> 9.2 6.4 ```

• Dicdactical note:

• I will illustrate the steps of the in-place partition algorithm using the following input:

inside the algorithm description !

• Main idea of the in-place partition algorithm:

• Maintain 3 segments in the array:

• Segment 1 contains values that are pivot
• Segment 2 contains values that are > pivot
• Segment 3 contains values that have not been processed
• There is an empty slot in the array between segment 2 and segment 3.

• The algorithm works as follows:

• If a[larger_I - 1] > pivot then: add a[larger_I - 1] to segment 2:

• If a[larger_I - 1] ≤ pivot then: add a[larger_I - 1] to segment 1:

• Algorithm to perform an in-place partitioning:

 ``` int partition( double[] a, int iLeft, int iRight ) { 1. Save the pivot in a help variable: pivot = a[iRight-1]; (Result: we also make a "hole" in the array !) 2. set up 2 indices to delineate the segments: smaller_I = iLeft; larger_I = iRight-1; (Result: we also make a "hole" in the array !) 3. while ( larger_I > smaller_I ) { (At this moment, we have this situation:) if ( a[larger_I - 1] > pivot ) { /* ====================================================== Put a[larger_I -1] in Segment 2 and move larger_I down ====================================================== */ a[larger_I] = a[larger_I -1]; larger_I--; (Result:) } else { /* ================================================== Put a[larger_I -1] in Segment 1 and move hole down ================================================== */ help = a[larger_I -1]; a[larger_I -1] = a[smaller_I]; a[smaller_I] = help; smaller_I++; (Result:) } } 4. Put the pivot in its place a[larger_I] = pivot; 5. Return the position of the pivot: return (larger_I); } ```

• partition() method in Java:

 ``` public static int partition( double[] a, int iLeft, int iRight ) { double pivot; double help; int smaller_I, larger_I; smaller_I = iLeft; larger_I = iRight-1; pivot = a[iRight-1]; // Hole is initially at a[iRight-1] while ( larger_I > smaller_I ) { if ( a[larger_I -1] > pivot ) { /* ================================================== Put a[larger_I -1] in the hole and move hole down ================================================== */ a[larger_I] = a[larger_I -1]; larger_I--; } else { /* ================================================= a[larger_I -1] <= pivot, so: Swap a[smaller_I] with a[larger_I -1] and move smaller_I up ================================================= */ help = a[larger_I -1]; a[larger_I -1] = a[smaller_I]; a[smaller_I] = help; smaller_I++; } } a[larger_I] = pivot; // Put pivot in the "hole" return larger_I; } ```

• Recall the in-place Quick Sort algorithm that uses the in-place partitioning algorithm:

 ``` /* ======================================================== Sort: a[iLeft] a[iLeft+1] .... a[iRight-1] ======================================================== */ void QuickSort( double[] a, int iLeft, int iRight ) { if ( iRight - iLeft ≤ 1 ) { // An empty array or an array of 1 element don't need sorting ! return; } k = partition( a, iLeft, iRight ); // Returns the pos. of pivit after partitioning QuickSort( a, iLeft, k ); // Sort left half of partitioned array QucikSort( a, k+1, iRight); // Sort right half of partitioned array } ```

(It's the same method above, I just repeated it for context)

• How to use QuickSort: (demo program)

 ``` public static void main( String[] args ) { double[] x = {6.4, 2.5, 9.2, 3.5, 8.9, 1.1, 7.5, 4.2} ; System.out.println("Before sort: " + Arrays.toString(x) + "\n" ); QuickSort.sort( x, 0, x.length ); // Quick sort System.out.println("\nAfter sort: " + Arrays.toString(x) ); } ```

• Example Program: (Demo above code)

How to run the program:

 Right click on link and save in a scratch directory To compile:   javac testProg.java To run:          java testProgx