排序算法(2)、经典算法(7):快速排序算法
来源:互联网 发布:淘宝店主首页寄语 编辑:程序博客网 时间:2024/06/05 00:36
概述
快速排序,确实是一种排序算法,归到排序算法类别下。
又因为确实是经典算法,也就归到经典算法类别下了。
时间复杂度,最好为O(n),最差为O(n^2),平均为O(nlog(n))。为什么?用递归的写法去推?
在快速排序算法中,使用了分治策略。首先把序列分成两个子序列,递归地对子序列进行排序,直到整个序列排序结束。
步骤如下:
在序列中选择一个关键元素做为轴;
对序列进行重新排序,将比轴小的元素移到轴的前边,比轴大的元素移动到轴的后面。在进行划分之后,轴便在它最终的位置上;
递归地对两个子序列进行重新排序:含有较小元素的子序列和含有较大元素的子序列。
以下,每次挑选pivor变量的时候,都是选的A[low]。并不是随机选的(即,不是随机快速排序。)
代码实现
多增加一个数组
快速排序的函数。
有个问题:快速排序为什么能保证,low>=high的时候结束,是正确的?
二分查找也是,为什么low>high的时候,查找就结束了?
什么时候low和high会交叉?
void quickSort(int * A, int low, int high) {// 当low>=high的时候,结束// 当low<high的时候,继续排序if (low < high){int midIdx = FindMidIdx(A,low,high); // 排序,并找到中轴元素的下标quickSort(A,low,midIdx-1); // 对左边的进行排序quickSort(A,midIdx+1,high); //对右边的进行排序}}
对于quickSort这个函数需要说明的是,先调用找中轴、排序的函数。把一个长的序列分成两个小的。
左边的都比中轴小,右边的都不比中轴小。
假设左右两边都是排好了的序列,那么整个序列也排列好了。于是再对左边、右边的序列分别再排。
int FindMidIdx(int * A, int low, int high) {int lowReserve = low; // 保存要对A的一部分排序的范围// 先开一个临时数组Bint N = high - low + 1; // B的大小int * B = new int[N];int j = 0; // 为了填充B,造出来的下标 B里的lowint k = N-1; // B里的high// 找中轴变量int pivor = A[low]; // A的low是中轴low++; // 从第二个开始起,开始找while (low<=high) // 从左往右扫{int temp = A[low];if (temp<pivor){B[j] = temp;j++;} else {B[k] = temp;k--;}low++;}B[j] = pivor;int midIdx = j + lowReserve; // A中的中间位置 for (int i = 0;i<N;i++) { A[lowReserve+i] = B[i]; }delete[] B;return midIdx;}还是那句话,用了更多的空间,换了简单的思维。开了一个临时数组B。
每次找中轴,都是找的一个数组中第1个元素。
从第二个开始扫描,直到末尾。把比较后的结果都放到B里面,最后把中轴变量放入B的“中间”位置,在代码中是用j来记录的。
最后,把B中的所有元素又赋值给A对应的部分。
需要注意的是,B是从0开始,到这次需要排序的数组的大小-1。而A是原始的数组。大小一直不变。
所以用下标访问的时候,访问B的下标和访问A的下标,写法是不同的 。
整个程序是这样的。
#include <iostream>using namespace std;const int global_N = 10;int FindMidIdx(int * A, int low, int high) {int lowReserve = low; // 保存要对A的一部分排序的范围int highReserve = high;// 先开一个临时数组Bint N = high - low + 1; // B的大小int * B = new int[N];int j = 0; // 为了填充B,造出来的下标 B里的lowint k = N-1; // B里的high// 找中轴变量int pivor = A[low]; // A的low是中轴low++; // 从第二个开始起,开始找while (low<=high) // 从左往右扫{int temp = A[low];if (temp<pivor){B[j] = temp;j++;} else {B[k] = temp;k--;}low++;}B[j] = pivor;int midIdx = j + lowReserve; // A中的中间位置 for (int i = 0;i<N;i++) { A[lowReserve+i] = B[i]; }delete[] B;return midIdx;}void quickSort(int * A, int low, int high) {// 当low>=high的时候,结束// 当low<high的时候,继续排序if (low < high){int midIdx = FindMidIdx(A,low,high); // 排序,并找到中轴元素的下标for (int i = 0;i<global_N;i++){cout<<A[i]<<" ";}cout<<"\n";quickSort(A,low,midIdx-1); // 对左边的进行排序quickSort(A,midIdx+1,high); //对右边的进行排序}}int main() {int A[global_N] = {4,2,66,88,3,1,7,49,1,10};cout<<"原始数据"<<endl;for (int i = 0;i<global_N;i++){cout<<A[i]<<" ";}cout<<"\n";cout<<"开始快速排序"<<endl;quickSort(A,0,global_N-1);system("pause");return 0;}原始数据
4 2 66 88 3 1 7 49 1 10
开始快速排序
2 3 1 1 4 10 49 7 88 66
1 1 2 3 4 10 49 7 88 66
1 1 2 3 4 10 49 7 88 66
1 1 2 3 4 7 10 66 88 49
1 1 2 3 4 7 10 49 66 88
有技巧的排序
如果在findMidIdx里面,不开辟临时数组B,就更有技巧了。
假设把A的第一个元素当作中轴变量pivor,就从A的右往左搜索,直到找到比pivor小的,放在low的位置,low++,再从左往右找,直到结束。
有个问题:快速排序为什么能保证,low>=high的时候结束,是正确的?
int findMidIdx(int A[], int low, int high) {int pivor = A[low]; // 第一个元素是pivorwhile (low < high){// 从右往左找while(A[high]>=pivor && low<high){high--; // 继续往左走} // 直到找到比pivor小的了,应该放在左边了A[low] = A[high];// low++; // 千万不能有这句话!// 从左往右找while(A[low]<pivor && low<high){low++;}A[high] = A[low];// high--;// 也千万不能有这句话!}int midIdx = low;A[midIdx] = pivor;return midIdx;}
quickSort函数与上面的一样。
当在main中对以下这个数组排序时
int A[g_N] = {4,2,66,88,3,1,7,49,1,10};
quickSort(A,0,g_N-1);
quickSort(A,0,g_N-1);
结果
1 2 1 3 4 88 7 49 66 101 2 1 3 4 88 7 49 66 10
1 1 2 3 4 88 7 49 66 10
1 1 2 3 4 10 7 49 66 88
1 1 2 3 4 7 10 49 66 88
1 1 2 3 4 7 10 49 66 88
代码比第一个版本要简洁很多。非常有技巧了。
注意,在赋值之后,low++和high--都是不能写的。如果这样写了以后,可能会跳过一些数据。
注意,在赋值之后,low++和high--都是不能写的。如果这样写了以后,可能会跳过一些数据。
同时,在while里从右往左或从左往右搜索时,当low>=high,都必须是结束条件。
low和high的变换,让while去控制就好了。
如何对数组进行引用?
int
n3[3] = {2, 4, 6};
int
(&rn3)[3] = n3;
// A reference to an array of 3 ints
选一个更好的pivor
有一个在一堆数中,找出第K大的元素的算法。递归调用。和findMidIdx的道理差不多。
之后的文章会总结这个算法,所以在这里就不多说了。
随机快速排序
随机快速排序,就是在每次选pivor的时候,随机地选一个数,而不是选A[low]。
那是不是每次都在选pivor之前,先随机生成一个下标,把A[下标]和A[low]交换,然后继续把A[low]作为pivor呢?
0 0
- 排序算法(2)、经典算法(7):快速排序算法
- 经典排序算法----快速排序算法(不稳定)
- 【经典算法】快速排序
- 经典算法:快速排序
- 经典算法--快速排序
- 经典算法--快速排序
- 【经典算法】:快速排序
- 【经典排序算法】快速排序
- 经典排序算法--快速排序
- [快速排序] 经典排序算法
- 经典算法之交换排序(冒泡排序、快速排序)
- php经典算法(二分法、快速排序)
- 快速排序(算法)
- 快速排序(算法)
- 快速排序(算法):
- (算法)快速排序
- 算法介绍(2) 快速排序算法
- 排序算法(四)快速排序算法
- 异步加载图片错位问题
- GOP/ 码流 /码率 / 比特率 / 帧速率 / 分辨率【转】
- Hibernate中Java对象的三种状态及其转换
- 有没有方法在安装apk时不出现安装界面
- 4-4 链式表的按序号查找 (10分)
- 排序算法(2)、经典算法(7):快速排序算法
- ZCMU-1261-采药
- POJ 1604 Just the Facts JAVA .
- thinkPHP常用数据操作(三)连贯操作
- python学习-1-列表,元组,字典,集合,文件操作
- 关于viewPager+radioGroup+Fragment嵌套,其中一个有listview,数据空白的问题
- 数学期望
- Linux程序设计复习
- xib约束问题 (涉及系统tabbar的高度问题)