快速排序的优化
来源:互联网 发布:淘宝ipad5.0.1版本 编辑:程序博客网 时间:2024/05/20 21:20
今天,我们组这周小会讲了讲算法导论上的快速排序优化问题;和大家分享,有什么问题,希望大家指正,谢谢!!!
1. 快速排序定义:
通过一趟排序将待排序数组分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
它是采用分治思想,进行排序;每次选取一个枢轴,确定枢轴的位置,将原数组分成两部分,进行递归排序;
2. 普通排序算法
以下是 严蔚敏 课本上的数据结构代码的实现:
#include "stdafx.h"#include <time.h>#include <iostream>#include <algorithm>using namespace std;const int LEN = 1000000; int Partition(int* arr, int low, int high){int temp = arr[low];while(low < high){while ( low < high && arr[high] >= temp ){high--;}arr[low] = arr[high];while ( low < high && arr[low] <= temp ){low++;}arr[high] = arr[low];}arr[low] = temp;return low;}//数组的首地址,最小下标和最大下标void QuickSort(int* arr, int low, int high){int pivotloc = -1;if ( high - low > 0 ){pivotloc = Partition(arr, low, high);QuickSort(arr, low, pivotloc-1);QuickSort(arr, pivotloc+1, high);}}int _tmain(int argc, _TCHAR* argv[]){int* arr = new int[LEN];srand((unsigned)time(NULL));for (int i = 0; i < LEN; i++){arr[i] = rand()%1000000;}QuickSort(arr,0,LEN-1);//输出结果cout<<"排序后的数组:"<<endl;for ( int i = 0; i< LEN; i++){cout<<arr[i]<<" ";}cout<<endl;return 0;}
但是,我们一般都是把low位置的元素作为枢轴,但是这样,可能不是最优的情况;
原因是:当数组有序时,待排序的时间复杂度为O(n^2);平均情况下为O(nlogn)。
3. 对枢轴选取的优化策略:
可分为
1、随即选择枢轴
////////////////////////////////////////////////////////////////////////////随机产生的枢轴//将数组中下标为index1和index2的两整数进行交换inline void Swap(int* arr, int index1, int index2){if ( arr[index1] == arr[index2] ){return;}int temp = arr[index1];arr[index1] = arr[index2];arr[index2] = temp;}//枢轴采用随机产生int Partition_RandPiv(int* arr, int low, int high){srand((unsigned)time(NULL));int index = rand()%(high-low) + low;Swap(arr, low, index);//将枢轴与low值交换int temp = arr[low];while(low < high){while ( low < high && arr[high] >= temp ){high--;}arr[low] = arr[high];while ( low < high && arr[low] <= temp ){low++;}arr[high] = arr[low];}arr[low] = temp;return low;}void QuickSort_RandPiv(int* arr, int low, int high){int pivotloc = -1;if ( high - low > 0 ){pivotloc = Partition_RandPiv(arr, low, high);QuickSort_RandPiv(arr, low, pivotloc-1);QuickSort_RandPiv(arr, pivotloc+1, high);}}
2、三数取中
//////////////////////////////////////////////////////////////////////////////3数取中//int MidNumOf3(int* arr, int n1, int n2, int n3){int temp1, temp2;if ( arr[n1] < arr[n2] ){temp1 = n1;temp2 = n2;}else{temp1 = n2;temp2 = n1;}if ( arr[n3] > arr[temp2] ){return temp2;}else if ( arr[n1] > arr[temp2] ){return temp1;}else{return n3;}}int Partition_Mid_3(int* arr, int low, int high){int minIndex = MidNumOf3(arr,low, low + ((high-low)>>1), high);Swap(arr,minIndex, low);int temp = arr[low];while(low < high){while ( low < high && arr[high] >= temp ){high--;}arr[low] = arr[high];while ( low < high && arr[low] <= temp ){low++;}arr[high] = arr[low];}arr[low] = temp;return low;}void QuickSort_Mid_3(int* arr, int low, int high){int pivotloc = -1;if ( high - low > 0 ){pivotloc = Partition_Mid_3(arr, low, high);QuickSort_Mid_3(arr, low, pivotloc-1);QuickSort_Mid_3(arr, pivotloc+1, high);}}
3、一次划分,将与枢轴相同的数字聚集到一起,减少递归次数
例如: 对数组{6,4,6,7,1,6,7,6,8,6}进行排序;
对第一次划分:6作为枢轴,划分后得到:{1,4,6,6,6,6,6,7,8,7};
然后再对{1,4},{7,8,7}递归进行排序。
原理:这样既可以减少划分次数,且降低了递归次数。
void Partition_Gather(int* arr, int low, int high, int& lowEnd, int& highStart){bool flag1 = true, falg2 = true;int start1=-1, end1=-1;int start2=-1, end2=-1;int nMove=-1;int temp = arr[low];while(low < high){while ( low < high ){if ( arr[high] > temp ){high--;}else if ( arr[high] == temp ){if ( flag1 ){end1 = high;start1 = end1;flag1 = false;}else{start1--;Swap(arr, high, start1);//两数交换}high--;}else{break;}}arr[low] = arr[high];while ( low < high ){if ( arr[low] < temp ){low++;}else if ( arr[low] == temp ){if ( falg2 ){start2 = low;end2 = low;falg2 = false;}else{end2++;Swap(arr, low, end2);}low++;}else{break;}}arr[high] = arr[low];}arr[low] = temp;//将与枢轴元素一样的聚集在一块nMove = low - 1;if ( start2 != -1 ){nMove = low -1;while ( nMove >= end2 && start2 <= end2 ){Swap(arr, nMove, start2);start2++;nMove--;}}lowEnd = nMove;nMove = low + 1;if ( start1 != -1 ){nMove = low +1;while ( nMove <= start1 && start1 <= end1 ){Swap(arr, nMove, end1);end1--;nMove++;}}highStart = nMove;}void QuickSort_Gather(int* arr, int low, int high){int pivotl = -1;int pivotr = -1;if ( low >= 0 && high - low > 0 ){Partition_Gather(arr, low, high, pivotl,pivotr);QuickSort_Gather(arr, low, pivotl);QuickSort_Gather(arr, pivotr, high);}}
4、实验时间比较
我们将上述几种,以及固定枢轴和STL中sort排序进行了时间统计,看看他们的效果如何:
我们对随机生成的10000000个整数进行了排序,效果如下:
时间比较 排序方法时间固定枢轴(为low)1770ms随机枢轴2560ms3数取中1924ms聚集相等元素1312msSTL中sort方法843ms
4. 对内存栈空间的优化
但由于如果数组很长,那么递归次数较多,而递归我们使用的是系统内存中栈,有可能出界。
因此,我们得考虑尽量减少使用栈的大小;
原理:使用各种方法,降低递归次数;
方法有:
1、与插入排序结合使用;
原理:当待排序的数据量较小时,插排的效率是较高的。因此,当将要排序的子序列为几个(8或10等均可)元素时,直接用插排;最终减少了递归的深度和次数;
2、循环代替递归;
原理:我们在递归处理当前待排序的两个子序列时,我们可以规定,仅第一个子序列使用递归处理,第二个子序列我们使用while循环去处理;如下所示:
void QuickSort(int* arr, int low, int high){int pivotloc = -1;if ( high - low > 0 ){pivotloc = Partition(arr, low, high);QuickSort(arr, low, pivotloc-1);low = pivotloc + 1;}}
这时,我们可能大家可能会想到对当前处理的一种优化策略:每次选取较长的待排子序列进行循环,而将较短的子序列进行递归处理;代码如下描述:
void QuickSort(int* arr, int low, int high){int pivotloc = -1;if ( high - low > 0 ){pivotloc = Partition(arr, low, high);if ( high - pivotloc > pivotloc - low ){QuickSort(arr, low, pivotloc-1);low = pivotloc + 1;}else{QuickSort(arr, pivotloc + 1, high);high = pivotloc -1;}}}3、上述第3中方法,每次将与枢轴相等元素聚集在一起;
这里将不再详述。参考上面的内容。4、使用并行或多线程处理子序列
利用并行计算来并行处理这些重复的子问题,使用Erlang可以非常方便非常清晰地利用这种方法解决问题。由于Erlang是一种声明式、函数式编程语言,所以要表述基本的快速排序算法非常方便:其中F是一个用于比较的函数。qsort(_, []) -> [];qsort(F, [H|T]) -> qsort(F, [X || X <- T, F(X, H)]) ++ [H] ++ qsort(F, [X || X <- T, not F(X, H)]).qsort(L) -> qsort(fun(X, Y) -> X < Y end, L).具体详述,请参考http://shiningray.cn/parallel-quick-sort-in-erlang.html,由于本人现在还没弄明白。
5. 参考文献
《算法导论》;
- 快速排序的优化
- 快速排序的优化
- 优化的快速排序
- 快速排序的优化
- 快速排序的优化
- 快速排序的优化
- 快速排序的优化
- 快速排序的优化
- 优化的快速排序
- 快速排序的优化
- 快速排序的优化
- 快速排序的优化
- 快速排序的优化算法
- 没有优化的快速排序
- 快速排序过程的优化
- java快速排序的优化
- 起泡排序的进一步优化:快速排序
- 三种快速排序以及快速排序的优化
- Delphi keydown与keyup、keypress的区别
- opencms 安装
- windows下搭建Android开发环境心得
- ramdisk驱动程序分析-2.6内核--块设备驱动框架(1)
- 大端模式和小端模式
- 快速排序的优化
- 机器学习中的数学
- Tyvj P2053(线段覆盖‘s精度误差&析构函数)
- 交叉验证(CrossValidation)方法思想
- Linux 内存管理系统:初始化
- uml总体认识
- 大叔控
- 在suse linux 企业版11下安装ORACLE10g_1_0_3详细过程
- POJ 1195 Mobile phones