多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## 2.4 Quicksort (Partition Exchange Sort) Next we look at a sorting algorithm, called "Quicksort," that was developed by Hoare (1962). Quicksort is similar to Mergesort in that the sort is accomplished by dividing the array into two partitions and then sorting each partition recursively. In Quicksort, however, the array is partitioned by placing all items smaller than some pivot item before that item and all items larger than the pivot item after it. The pivot item can be any item, and for convenience we will simply make it the first one. The following example illustrates how Quicksort works. Example 2.3**![Start example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**Suppose the array contains these numbers in sequence: ![](https://box.kancloud.cn/6295e3a7ccd728e7dfe5c1fad44dacfe_260x69.jpg) 1. Partition the array so that all items smaller than the pivot item are to the left of it and all items larger are to the right: ![](https://box.kancloud.cn/7aeb295f4a6e7536e4a4724c4d1fb08b_241x97.jpg) 2. Sort the subarrays ![](https://box.kancloud.cn/24274dd015dd901a27e561f5ba0cc679_241x94.jpg) **![End example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)** After the partitioning, the order of the items in the subarrays is unspecified and is a result of how the partitioning is implemented. We have ordered them according to how the partitioning routine, which will be presented shortly, would place them. The important thing is that all items smaller than the pivot item are to the left of it, and all items larger are to the right of it. Quicksort is then called recursively to sort each of the two subarrays. They are partitioned, and this procedure is continued until an array with one item is reached. Such an array is trivially sorted. Example 2.3 shows the solution at the problem-solving level. [Figure 2.3](#ch02fig03) illustrates the steps done by a human when sorting with Quicksort. The algorithm follows. [![Click To expand](https://box.kancloud.cn/caaa6261e8c2b41a7df5ec236570b2fe_350x253.jpg)](fig2-3_0.jpg) Figure 2.3: The steps done by a human when sorting with Quicksort. The subarrays are enclosed in rectangles whereas the pivot points are free. Algorithm 2.6: Quicksort**![Start example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**Problem: Sort *n* keys in nondecreasing order. Inputs: positive integer *n*, array of keys *S* indexed from 1 to *n.* Outputs: the array *S* containing the keys in nondecreasing order. ``` void quicksort (index low, index high) { index pivotpoint; if (high > low){ partition(low, high, pivotpoint); quicksort(low, pivotpoint - 1); quicksort(pivotpoint + 1, high); } } ``` **![End example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)** Following our usual convention, *n* and *S* are not parameters to procedure *quicksort.* If the algorithm were implemented by defining *S* globally and *n* was the number of items in *S*, the top-level call to *quicksort* would be as follows: *quicksort*(1, *n*); The partitioning of the array is done by procedure *partition.* Next we show an algorithm for this procedure. Algorithm 2.7: Partition**![Start example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**Problem: Partition the array *S* for Quicksort. Inputs: two indices, *low* and *high*, and the subarray of *S* indexed from *low* to *high.* Outputs: *pivotpoint*, the pivot point for the subarray indexed from *low* to *high.* ``` void partition (index low, index high, index& pivotpoint) { index i, j; keytype pivotitem; pivotitem = S[low]; // Choose first item for j = low; //pivotitem. for (i = low + 1; i <= high; i++) if (S[i] < pivotitem){ j++; exchange S[i] and S[j]; } pivotpoint = j; exchange S[low] and S[pivotpoint]; // Put pivotitem at pivotpoint. } ``` **![End example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)** Procedure *partition* works by checking each item in the array in sequence. Whenever an item is found to be less than the pivot item, it is moved to the left side of the array. [Table 2.2](#ch02table02) shows how *partition* would proceed on the array in Example 2.3. Table 2.2: An example of procedure *partition*\[[\*](#ftn.ch02fnt02)\]*i* *j* *S*\[1\] *S*\[2\] *S*\[3\] *S*\[4\] *S*\[5\] *S*\[6\] *S*\[7\] *S*\[8\] — — 15 22 13 27 12 10 20 25 ← Initial values 2 1 **15** **22** 13 27 12 10 20 25 3 2 **15** 22 **13** 27 12 10 20 25 4 2 **15** 13 22 **27** 12 10 20 25 5 3 **15** 13 22 27 **12** 10 20 25 6 4 **15** 13 12 27 22 **10** 20 25 7 4 **15** 13 12 10 22 27 **20** 25 8 4 **15** 13 12 10 22 27 20 **25** — 4 10 13 12 15 22 27 20 25 ← Final values \[[\*](#ch02fnt02)\]Items compared are in boldface. Items just exchanged appear in squares. Next we analyze Partition and Quicksort. **![Start Sidebar](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**Analysis of [Algorithm 2.7](#ch02ex13) Every-Case Time Complexity (Partition)Basic operation: the comparison of *S\[i\]* with *pivotitem.* Input size: *n* = *high* − *low* + 1, the number of items in the subarray. Because every item except the first is compared, *T*(*n*) = *n* − 1. We are using *n* here to represent the size of the subarray, not the size of the array *S.* It represents the size of *S* only when *partition* is called at the top level. **![End Sidebar](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)** Quicksort does not have an every-case complexity. We will do worst-case and average-case analyses. **![Start Sidebar](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**Analysis of [Algorithm 2.6](#ch02ex12) Worst-Case Time Complexity (Quicksort)Basic operation: the comparison of *S\[i\]* with *pivotitem* in *partition.* Input size: *n*, the number of items in the array *S.* Oddly enough, it turns out that the worst case occurs if the array is already sorted in nondecreasing order. The reason for this should become clear. If the array is already sorted in nondecreasing order, no items are less than the first item in the array, which is the pivot item. Therefore, when *partition* is called at the top level, no items are placed to the left of the pivot item, and the value of *pivotpoint* assigned by *partition* is 1. Similarly, in each recursive call, *pivotpoint* receives the value of *low.* Therefore, the array is repeatedly partitioned into an empty subarray on the left and a subarray with one less item on the right. For the class of instances that are already sorted in nondecreasing order, we have ![](https://box.kancloud.cn/60fe85245440925295b6f0b76f16c9df_400x68.jpg) We are using the notation *T(n)* because we are presently determining the every-case complexity for the class of instances that are already sorted in nondecreasing order. Because *T*(0) = 0, we have the recurrence ![](https://box.kancloud.cn/0387ad8deb68c54cf488d7f58daa2cb4_330x69.jpg) This recurrence is solved in Example B.16 in [Appendix B](LiB0102.html#1369). The solution is ![](https://box.kancloud.cn/8de34f7b741ecece200231d981b244a7_146x42.jpg) We have established that the worst case is at least *n(n* − 1)/2. Although intuitively it may now seem that this is as bad as things can get, we still need to show this. We will accomplish this by using induction to show that, for all *n*, ![](https://box.kancloud.cn/5873c2d8ceb7d9723684fbcf6ca08610_152x42.jpg) Induction base: for *n* = 0 ![](https://box.kancloud.cn/1c659eef02fc765035a2b155a42ea6af_179x42.jpg) Induction hypothesis: Assume that, for 0 ≤ *k* < *n*, ![](https://box.kancloud.cn/9be977de39065cf2a2a11dca91b2a7d6_148x42.jpg) Induction step: We need to show that ![](https://box.kancloud.cn/1e6b1471230093a9dfb4d00412227f65_152x42.jpg) For a given *n*, there is some instance with size *n* for which the processing time is *W(n).* Let *p* be the value of *pivotpoint* returned by *partition* at the top level when this instance is processed. Because the time to process the instances of size *p* − 1 and *n* − *p* can be no more than *W(p* − 1) and *W(n* − *p*), respectively, we have ![](https://box.kancloud.cn/acb6ab5046f1cd1c9a6c18149b5ade13_400x65.jpg) The last inequality is by the induction hypothesis. Algebraic manipulations can show that for 1 ≤ *p* ≤ *n* this last expression is ![](https://box.kancloud.cn/4a8ad0d2bf604495e6552944a905b03d_100x41.jpg) This completes the induction proof. We have shown that the worst-case time complexity is given by ![](https://box.kancloud.cn/cd56a9f4739e1046bb3eca8669bec483_228x42.jpg) **![End Sidebar](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)** The worst case occurs when the array is already sorted because we always choose the first item for the pivot item. Therefore, if we have reason to believe that the array is close to being sorted, this is not a good choice for the pivot item. When we discuss Quicksort further in [Chapter 7](LiB0052.html#627), we will investigate other methods for choosing the pivot item. If we use these methods, the worst case does not occur when the array is already sorted. But the worst-case time complexity is still *n(n* − 1)/2. In the worst case, [Algorithm 2.6](#ch02ex12) is no faster than Exchange Sort ([Algorithm 1.3](LiB0008.html#32)). Why then is this sort called Quicksort? As we shall see, it is in its average-case behavior that Quicksort earns its name. **![Start Sidebar](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**![](https://box.kancloud.cn/4ec3b9ba902661b4cb645907d775d785_27x26.jpg) Analysis of [Algorithm 2.6](#ch02ex12) Average-Case Time Complexity (Quicksort)Basic operation: the comparison of *S\[i\]* with *pivotitem* in *partition* Input size: *n*, the number of items in the array *S.* We will assume that we have no reason to believe that the numbers in the array are in any particular order, and therefore that the value of *pivotpoint* returned by *partition* is equally likely to be any of the numbers from 1 through *n.* If there was reason to believe a different distribution, this analysis would not be applicable. The average obtained is, therefore, the average sorting time when every possible ordering is sorted the same number of times. In this case, the average-case time complexity is given by the following recurrence: ![](https://box.kancloud.cn/a4345060b69f6ca9d68ecf1e05025c6f_400x119.jpg) In the exercises we show that ![](https://box.kancloud.cn/e93c1fd74b201ff5d5234084b4c1fc7d_349x55.jpg) Plugging this equality into [Equality 2.1](#ch02eqn01) yields ![](https://box.kancloud.cn/0ecf35349a1144cce85fd62a458479b6_253x55.jpg) Multiplying by *n* we have ![](https://box.kancloud.cn/5943b71495e2372c3566008cdbd9be1b_400x53.jpg) Applying [Equality 2.2](#ch02eqn02) to *n* − 1 gives ![](https://box.kancloud.cn/f20bc7c9a330da1944dc48cd196aec51_400x47.jpg) Subtracting [Equality 2.3](#ch02eqn03) from [Equality 2.2](#ch02eqn02) yields ![](https://box.kancloud.cn/617e25ff9a8b005b689c8a365db29b11_400x22.jpg) which simplifies to ![](https://box.kancloud.cn/ca8bbf63be077c6d53ac686d39525992_248x47.jpg) If we let ![](https://box.kancloud.cn/4d3065f0274b48cd3de65db739a7f7c7_97x43.jpg) we have the recurrence ![](https://box.kancloud.cn/94d3c6a05614072a235df0a5ecd07ebd_285x68.jpg) Like the recurrence in Example B.22 in [Appendix B](LiB0102.html#1369), the approximate solution to this recurrence is given by ![](https://box.kancloud.cn/e099953922b3266ea528e14515f6985b_92x21.jpg) which implies that ![](https://box.kancloud.cn/639716e4391976abd8f10540d9160bfc_345x49.jpg) **![End Sidebar](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)** Quicksort's average-case time complexity is of the same order as Mergesort's time complexity. Mergesort and Quicksort are compared further in [Chapter 7](LiB0052.html#627) and in Knuth (1973).