Skip to main content

Section 7.8 The Quick Sort

The quick sort uses divide and conquer to gain the same advantages as the merge sort, while not using additional storage. As a trade-off, however, it is possible that the list may not be divided in half. When this happens, we will see that performance is diminished.
A quick sort first selects a value, which is called the pivot value. Although there are many different ways to choose the pivot value, we will simply use the first item in the list. The role of the pivot value is to assist with splitting the list. The actual position where the pivot value belongs in the final sorted list, commonly called the split point, will be used to divide the list for subsequent calls to the quick sort.
Figure 7.8.1 shows that 54 will serve as our first pivot value. Since we have looked at this example a few times already, we know that 54 will eventually end up in the position currently holding 31. The partition process will happen next. It will find the split point and at the same time move other items to the appropriate side of the list, either less than or greater than the pivot value.
A single row of numbers shown in boxes with the number 54 highlighted and labeled as ’54 will be the first pivot value’. This image is used to illustrate the initial step in the Quick Sort algorithm where a pivot value is chosen to partition the list. The rest of the numbers in the row are in lighter boxes and include 26, 93, 17, 77, 31, 44, 55, and 20.
Figure 7.8.1. The First Pivot Value for a Quick Sort.
Partitioning begins by locating two position markers — let’s call them leftmark and rightmark — at the beginning and end of the remaining items in the list (positions 1 and 8 in Figure 7.8.2). The goal of the partition process is to move items that are on the wrong side with respect to the pivot value while also converging on the split point. Figure 7.8.2 shows this process as we locate the position of 54.
A series of diagrams illustrating the process of finding the split point for the number 54 in a Quick Sort algorithm. The diagrams show a row of numbers with two markers, ’leftmark’ and ’rightmark’, that move towards each other from opposite ends. Each step of the process is annotated with instructions like ’leftmark and rightmark will converge on split point’, ’26 <54 move to right, exchange 20 and 93, and so on. The movement of the markers and the exchanges of numbers are depicted with arrows and highlighted actions, detailing how the algorithm partitions the data around the pivot. The final diagram shows the leftmark and rightmark crossing over, indicating the split point has been found, and the pivot, 54, is in its correct position.
Figure 7.8.2. Finding the Split Point for 54.
We begin by incrementing leftmark until we locate a value that is greater than the pivot value. We then decrement rightmark until we find a value that is less than the pivot value. At this point we have discovered two items that are out of place with respect to the eventual split point. For our example, this occurs at 93 and 20. Now we can exchange these two items and then repeat the process again.
At the point where rightmark becomes less than leftmark, we stop. The position of rightmark is now the split point. The pivot value can be exchanged with the contents of the split point and the pivot value is now in place (Figure 7.8.3). In addition, all the items to the left of the split point are less than the pivot value, and all the items to the right of the split point are greater than the pivot value. The list can now be divided at the split point and the quick sort can be invoked recursively on the two halves.
The image shows the final step in the partitioning process of the Quick Sort algorithm, where the split point for the pivot value 54 is established. Two separate groups of numbers are displayed. The first group to the left includes numbers less than 54: 31, 26, 20, 17, and 44, with the annotation ’quicksort left half’. The second group to the right includes numbers greater than 54: 77, 55, and 93, with the annotation ’quicksort right half’. The number 54 is positioned in the center, labeled ’54 is in place’, indicating that it is at its correct position in the sorted array.
Figure 7.8.3. Completing the Partition Process to Find the Split Point for 54.
The quickSort function shown in Task 7.8.1.a invokes a recursive function, quickSortHelper. quickSortHelper begins with the same base case as the merge sort. If the length of the list is less than or equal to one, it is already sorted. If it is greater, then it can be partitioned and recursively sorted. The partition function implements the process described earlier. The following program sorts the list that was used in the example above.

Exploration 7.8.1. Quick Sort.

(a) C++ Implementation.

(b) Python Implementation.

The visualization in Figure 7.8.4 allows you to step through the algorithm.
Figure 7.8.4. Quick sort animation.
The visualization in Figure 7.8.5 shows how quick sort works in action. Our pivot is represented by the arrow on screen. If an object is bigger than the pivot, it will turn blue and stay where it is. If it is smaller it will turn red and swap to the left side of the pivot. Once an object is sorted, it will turn yellow.
Figure 7.8.5. Video of quickSort in action.
To analyze the quickSort function, note that for a list of length n, if the partition always occurs in the middle of the list, there will again be \(\log n\) divisions. In order to find the split point, each of the n items needs to be checked against the pivot value. The result is \(n\log n\text{.}\) In addition, there is no need for additional memory as in the merge sort process.
Unfortunately, in the worst case, the split points may not be in the middle and can be very skewed to the left or the right, leaving a very uneven division. In this case, sorting a list of n items divides into sorting a list of 0 items and a list of \(n-1\) items. Then sorting a list of \(n-1\) divides into a list of size 0 and a list of size \(n-2\text{,}\) and so on. The result is an \(O(n^{2})\) sort with all of the overhead that recursion requires.
We mentioned earlier that there are different ways to choose the pivot value. In particular, we can attempt to alleviate some of the potential for an uneven division by using a technique called median of three. To choose the pivot value, we will consider the first, the middle, and the last element in the list. In our example, those are 54, 77, and 20. Now pick the median value, in our case 54, and use it for the pivot value (of course, that was the pivot value we used originally). The idea is that in the case where the the first item in the list does not belong toward the middle of the list, the median of three will choose a better “middle” value. This will be particularly useful when the original list is somewhat sorted to begin with. We leave the implementation of this pivot value selection as an exercise.

Reading Questions Reading Questions

1.

    Given the following list of numbers [14, 17, 13, 15, 19, 10, 3, 16, 9, 12] which answer shows the contents of the list after the second partitioning according to the quicksort algorithm?
  • [9, 3, 10, 13, 12]
  • It’s important to remember that quicksort works on the entire list and sorts it in place.
  • [9, 3, 10, 13, 12, 14]
  • Remember quicksort works on the entire list and sorts it in place.
  • [9, 3, 10, 13, 12, 14, 17, 16, 15, 19]
  • The first partitioning works on the entire list, and the second partitioning works on the left partition not the right.
  • [9, 3, 10, 13, 12, 14, 19, 16, 15, 17]
  • The first partitioning works on the entire list, and the second partitioning works on the left partition.

2.

    Given the following list of numbers [1, 20, 11, 5, 2, 9, 16, 14, 13, 19] what would be the first pivot value using the median of 3 method?
  • 1
  • The three numbers used in selecting the pivot are 1, 9, 19. 1 is not the median, and would be a very bad choice for the pivot since it is the smallest number in the list.
  • 9
  • Good job.
  • 16
  • although 16 would be the median of 1, 16, 19 the middle is at len(list) // 2.
  • 19
  • the three numbers used in selecting the pivot are 1, 9, 19. 9 is the median. 19 would be a bad choice since it is almost the largest.
You have attempted of activities on this page.