排序算法——快速排序
来源:互联网 发布:oracle 数据库高可用 编辑:程序博客网 时间:2024/05/01 22:57
快速排序说白了就是一个不断填坑的过程!其基本思想是基于分治法的:在待排序表A[1...n]中任取一个元素pivot作为基准,通过一趟排序将待排序的表划分为独立的两部分A[1...k-1]和A[k+1...n],使得L[1...k-1]中所有元素都小于pivot,而相应地A[k+1...n]中的所有元素都大于等于pivot,并将pivot放置于A[k]处,这个过程就称之为一趟快速排序。然后递归地对两个子表重复上述过程,直到每部分内只有一个元素或空为止!
简单说来就是:
1.先从数组中找一个基准元素,一般选第一个或者最后一个,也可以随机选一个;
2.进行划分,将比基准数小的放到它左边,不小于基准的全放到它右边;
3.对左右俩子区间继续之前的方法进行划分,直到子区间只有一个元素或者为空;
其主要过程如下:
template <typename T> //此处用模板函数纯粹属于装X...大家可以直接写个int数组void QuickSort(T A[], int low, int high){if (low < high) {int pivotPos = Partition(A, low, high);QuickSort(A, low, pivotPos-1);QuickSort(A, pivotPos+1, high);}}
划分过程常用的有两种思路:
1)俩索引分别从首尾向中间扫描:标准的填坑思路!易于理解:
template <typename T>int Partition(T A[], int low, int high){T pivot = A[low];//先挖最低位的坑作为基准元素while (low < high) {while (low < high && A[high] >= pivot) --high;A[low] = A[high];//填补低位留下的坑while(low < high && A[low] < pivot) ++low;A[high] = A[low];//填补高位留下的坑}A[low] = pivot;//最终划分处的坑,填完收工!return low;}
假设有如下初始列:
0 1 2 3 4 5 6 7 //索引号
3 8 7 1 2 5 6 4 //第一步:令基准pivot = A[0],这不相当于在A[0]处挖了个坑吗,不过要把挖出来的保存到pivot中
2 8 7 1 2 5 6 4 //第二步:从右往左数找到第一个小于3(即pivot)的数即A[4],用它来填上一步A[0]留下的坑
2 8 7 1 8 5 6 4 //第三步:从左往右数找到第一个不小于3(即pivot)的数即A[1],用它来填上一步A[4]留下的坑
以此类推...自己在纸上画画...懒得写了
2 1 3 7 8 5 6 4 //最终结果!最后一步,用pivot保存的东东来填A[2]处留下的坑
填坑结束,打完收工!
template <typename T>int Partition(T A[], int low, int high){T pivot = A[high]; //以最后一个元素,A[high]为主元int i = low - 1;for (int j = low; j <= high-1; ++j) {if (A[j] < pivot) {++i;swap(A[i], A[j]);//即保证索引i及i之前的所有元素都是小于基准元素(pivot)的!//注意通过此种方式,划分之后,pivot左边的元素相对顺序是不变的!}}swap(A[i+1], A[high]);return i+1;}
一次划分过程如下:仍然用上面的那个数组
3 8 7 1 2 5 6 4 //选择最后的一个元素4作为基准,3与3交换,不用移动元素,比较了1次
3 1 7 8 2 5 6 4 //8与1交换,比较了3次
3 1 2 8 7 5 6 4 //7与2交换,比较了1次
3 1 2 4 7 5 6 8 //8与4交换,比较了2次
注意看着哦,经过一次划分后,基准元素4左边的元素(即2 1 3)的相对顺序是保持不变的!
快排的算法性能:
(1).空间效率:由于上述过程是递归的,所以需要一递归工作栈来保存递归调用信息。平均情况下,栈的深度为;
(2).时间复杂度:快排运行时间与划分是否对称有关。当初始序列基本有序或者基本逆序时,此时得到的划分极不对称,就得到最坏情况下的时间复杂度,就这样竟然叫做快速排序。。而当每次划分都很对称时,就得到最好的情况,此时复杂度为;不幸中的万幸就是,快排平均情况下的运行时间与其最佳状况的运行时间是很接近的,故它的平均时间复杂度也为
这里提几个改进快排的思路:
1)当递归过程中得到的子序列规模较小时就不在继续递归调用快排了,而是直接采用插入排序来进行后续的工作;
2)尽量选取一个可以将数据中分的基准元素;比如说可以在序列头尾和中间取3个元素,然后用他们的中位值来作为基准;当然也可以随机从序列中选取基准元素,这样做可以使得最坏情况在实际排序中发生的概率极低;
(3).稳定性:显而易见,快排是不稳定的,不过上面所讲的第二种划分方法得到了部分顺序保持的序列,还是值得注意的~
- 排序算法—快速排序
- 排序算法—快速排序
- 排序算法———快速排序
- 排序算法(一)——快速排序
- 排序算法——快速排序
- 排序算法——快速排序
- 经典排序算法——快速排序
- 排序算法——快速排序
- 经典排序算法——快速排序
- 经典排序算法——快速排序
- 排序算法5——快速排序
- 排序算法——快速排序
- 排序算法——快速排序
- 排序算法——快速排序
- c++排序算法——快速排序
- 算法——排序之快速排序
- 排序算法——快速排序
- 排序算法——快速排序
- 网络管理常用命令之三 - Netstat 命令详解(图文)
- 算法:从数组中查找重复的元素并分组
- CSS的选择器,优先级
- Android中Service的简单讲解
- 并查集学习入门到熟悉
- 排序算法——快速排序
- Spring MVC简介
- HDU 1285 确定比赛名次(拓扑排序)
- Java进阶(三十九)Java集合类的排序,查找,替换操作
- POJ 1912 A highway and the seven dwarfs(O(log N)求直线与凸包是否相交)
- key-value数据库的一种实现
- linux学习第五篇:安装nginx
- qsort与sort的七种用法!
- 使用HTML5技术控制电脑或手机上的摄像头