排序算法之快速排序
-
做算法作业的时候遇到了一道题目,题目答案里提示和快速排序的时间复杂度相同,便动手学习了一下快速排序。快速排序的主要思想应该也是分治。就是在原数组a中选择一个值作为枢纽(pivot),然后把所有比它小的值放在它左边,把比它大的值放在右边。然后再用类似的方法递归求解。值得注意的是,当第一个枢纽找到了它的位置后,比如下标为k,那么它在之后的递归过程中是不会改变的,因为它就应该在这个位置。
-
以下是我写的程序。
#include <iostream> using namespace std; void quicksort(int a[], int left, int right) { if (left > right) return; int i = left, j = right, pivot = a[left]; //i是左边哨兵,j是右边哨兵,pivot储存枢纽 while (i != j) { while (a[j] >= pivot && i < j) //先从右边开始找比枢纽小的 j--; while (a[i] <= pivot && i < j) //再从左边开始找比枢纽大的 i++; if (i < j) //交换两个数在数组中的位置 { int tmp = a[i]; a[i] = a[j]; a[j] = tmp; } } a[left] = a[i]; //最终将基准数归位 a[i] = pivot; quicksort(a, left, i - 1); //继续处理左边的,i已经是枢纽最终的位置,不会再改变 quicksort(a, i + 1, right); //继续处理右边的 } int main() { int a[11] = {0, 6, 1, 2, 7, 9, 3, 4, 5, 10, 8}; //一共十个数,a[0]弃置 quicksort(a, 1, 10); //快速排序调用 for (int i = 1; i <= 10; i++) //输出排序后的结果 cout << a[i] << " "; cout << endl; return 0; } -
程序运行结果
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YhvdUkhi-1618287083590)(排序算法之快速排序.assets/3.png)]](https://i-blog.csdnimg.cn/blog_migrate/1552c4e4d9cd091faa8fbc5edad4fbd9.png)
-
值得注意的是,这个程序的在把枢纽小的数放在枢纽左边,把枢纽大的数放在枢纽右边这个过程中的方法是比较独特的。和之前数据结构书本上的疯狂交换不同,它先从右边开始找,找到一个比枢纽小的数,然后停止,定位在哪个下标,之后再从左边开始找,找到一个比枢纽大的数,然后停止,定位在哪个下标。很显然,这两个数的位置都是不对的,因为比枢纽小的应该在枢纽左边,而它在右边,比枢纽大的应该在右边,而它在左边。所以直接把这两个位置不正确的数据进行交换即可。这样一下子解决两个数据。所以在这个过程中,我们是没有去管枢纽的,所以省去了把枢纽换来换去的时间,提升了程序效率。
-
下面我举一下第一个枢纽“6”的找到它真正位置的过程。
6 1 2 7 9 3 4 5 10 8 //把第一个位置的值(6)看作枢纽 i = 1; j = 10; //左哨兵和右哨兵 右哨兵先开始寻找第一个比枢纽小的数字,至于为什么是右哨兵开始,等等会解释 j--; ... 当j = 8的时候发现5比枢纽小,j停止 i++; ... 当 i = 4的时候发现7比枢纽大,i停止 比较i 和 j是不是相等,发现不相等 于是把这两个数进行交换 6 1 2 5 9 3 4 7 10 8 j--; 当 j = 7的时候发现4比枢纽小,j停止 i++; 当 i = 5 的时候发现9比枢纽大, i停止 i 和 j不相等,进行交换 6 1 2 5 4 3 9 7 10 8 j--; 当j = 6时发现3比枢纽小,j停止 i++; i 变成 6,这时候 由于 i == j 退出循环(程序13行和程序9行) 之后把枢纽和i位置的数据一换,变成 3 1 2 5 4 6 9 7 10 8 我们惊奇的发现枢纽成功找到了它的位置,且它左边的数都比他小,右边的数都比他大。 -
我们再来讲一讲为什么一开始一定要j也就是右哨兵开始动,找到一个比枢纽小的值,然后再左哨兵动,找到一个比枢纽大的值,他们反正最后都是交换,谁先动不是都一样的嘛?
-
原因就在于当 i == j 的时候跳出循环后,枢纽需要和 i 的位置进行交换。一般来说我们的枢纽都是选择数组的第一个元素,所以这样交换,需要满足一个条件,那就是 第 i 个位置的元素比枢纽的值小。而我们如果先让右哨兵动,让它一直自减,直到遇到一个比枢纽小的数,然后左哨兵才动,一直自增,最后i == j,但是这个位置是右哨兵找到的,只是左哨兵撞上了,所以 i 或者说 j 这个位置的值是小于枢纽的。所以跳出while循环后,枢纽和 i 位置的元素的交换是正确的。
-
那怕你不够信服,我们就来模拟一下,如果左哨兵先动会发生什么。
6 1 2 7 9 3 4 5 10 8 //把第一个位置的值(6)看作枢纽 i = 1; j = 10; //左哨兵和右哨兵 i++; ... 当 i = 4的时候发现7比枢纽大,i停止 j--; ... 当j = 8的时候发现5比枢纽小,j停止 比较i 和 j是不是相等,发现不相等 于是把这两个数进行交换 6 1 2 5 9 3 4 7 10 8 i++; 当 i = 5 的时候发现9比枢纽大, i停止 j--; 当 j = 7的时候发现4比枢纽小,j停止 i 和 j不相等,进行交换 //我们看到到这里所有的结果和右哨兵开始的结果都是一样的,但是马上就会产生区别 6 1 2 5 4 3 9 7 10 8 i++; ... 当i = 7的时候 发现 9大于枢纽, i停止 因为 j = 7,所以j不能再自减了,直接跳出while循环 之后把枢纽和i位置的数据一换,变成 9 1 2 5 4 3 6 7 10 8 我们看到在最后一步,也就是枢纽和 i 位置的元素交换时,产生了错误![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GXruD5ej-1618287083593)(排序算法之快速排序.assets/4.png)]](https://i-blog.csdnimg.cn/blog_migrate/2400d7cbffea551b014ac71f0786d9e1.png)
-
所以快速排序算法我们需要注意几个问题:如何把比枢纽小的换到左边,比枢纽大的放到右边 以及 右哨兵先动还是左哨兵先动。
-
参考链接
160

被折叠的 条评论
为什么被折叠?



