偶然在书上看到句话,“为C++程序员所津津乐道的一件事,就是sort()全面打败c语言的quicksort”,于是决定亲自测试下,正好将其他的排序也一并测试了,当做当初没写的补偿吧。
测试文件的一些说明:
1、 测试中如果用数组,当MAX达到1000000时,会stack over,所以这里全部用了vector。
2、随机数用一个全局vector保存,单独排序时直接vector<int> v(orited),就这但来说c++确实很方便啊,因为有的递归调用,调用过程中vector为多大都不确定,用数组怎么初始化就成为了一个问题。
3、测试排序的函数命名全部为下划线分割的方式,如select_sort()。
4、排序结果经过写入文件验证。
5、文件在win7,vs6,vs08,以及gcc下编译通过。vs08下,最初用的文件操作用FILE,会有错,可以加以修改为stream,比如打开和判空修改如下:
FILE *fp =fopen("data.txt","wr"); ofstream fp("data.txt");
if (fp==NULL) if (fp.fail())写入文件两个的对应部分,当然读出的时候 ip >> temp; v.push_back(temp);即可
fp << orited[i] << " ";//这里最好加上个空格,不然文件中连起来的数字也没意义 fprintf(fp,"%d ",orited[i]); //关闭 // fclose(fp); fp.close();
最终修改的标准c++测试文件如下
//============================================================================ // Name : sort.cpp // Author : xia // Version : 1.0 // Copyright : NUAA // Description : 各种排序算法的比较(冒泡,选择,快速,希尔,堆,归并以及algorithm的sort) //============================================================================ #include <iostream> #include <fstream> #include <algorithm> #include <vector> #include <ctime> using namespace std; const int MAX = 1000;//当MAX为一百万,用数组会stack overflow,所以全部用vector vector<int> orited;//全局原始未排序vector void randNum() {//产生随机数存于vector ofstream fp("data.txt"); if (fp.fail()) { cout << "open data error " << endl; exit(EXIT_FAILURE); } srand((unsigned)time(NULL)); for (int i=0 ; i<MAX ; i++) { orited.push_back(rand()); //是用vector保存随机数 fp << orited[i] << " "; } fp.close(); } /************************************************************************/ /* 快速排序 */ /************************************************************************/ int Partion(vector<int> &v ,int low ,int high) {//对vector进行划分,返回枢轴下标 int pivotkey; pivotkey = v[low] ; while ( low < high ) { while (low < high && v[high] >= pivotkey) high -- ; v[low] = v[high]; while (low < high && v[low] <= pivotkey ) low ++ ; v[high] = v[low]; } v[low] = pivotkey ; return low ; } void quickSort(vector<int> &number ,int left ,int right) {//用引用解决传参问题,以下类似 if ( left < right ) { int i = Partion(number , left, right) ; quickSort(number, left, i-1); // 对左边进行递归 quickSort(number, i+1, right); // 对右边进行递归 } } void quick_sort() {//快速排序测试程序 double start ,end ; vector<int> v(orited);//直接用全局orited初始化v start = clock() ; quickSort(v,0,MAX-1); end = clock(); cout << "quick sort cost : " << end - start << endl; } /************************************************************************/ /* 插入排序 */ /************************************************************************/ void InsertSort(vector<int> &v) { //对vector做插入排序 int i,j; int key; for(i=1 ; i < v.size() ; i++) if ( v[i] < v[i-1] )// "<",将v[i]插入有序子表 { key = v[i]; for( j=i-1 ; key < v[j] ; j-- ) v[j+1] = v[j]; // 记录后移 v[j+1] = key; // 插入到正确位置 } } void insert_sort() { vector<int> v(orited); double start ,end; start = clock(); InsertSort(v); end = clock(); cout << "insert sort cost : " << end - start << endl; } /************************************************************************/ /* algorithm的sort方法 */ /************************************************************************/ void vector_sort() { double start ,end ; vector<int> v(orited); start = clock(); sort(v.begin(),v.end()); end = clock(); cout << "vector sort cost : " << end - start << endl; } /************************************************************************/ /* 冒泡排序 */ /************************************************************************/ void bubble_sort() { int i,j; vector<int> v(orited); double start ,end; start = clock(); for ( i=0 ; i< MAX ; i++) { for (j=i+1 ; j< MAX ; j++) { if (v[i] > v[j])//最小的往左冒泡 { swap(v[i],v[j]); } } } end = clock(); cout << "bubble sort cost : " << end - start << endl; } /************************************************************************/ /* 选择排序 */ /************************************************************************/ void select_sort() { int i,j,min; vector<int> v(orited); double start ,end; start = clock(); for (i=0 ; i< MAX ; i++) { min = i ; for (j=i+1 ; j<MAX ; j++) { if (v[j] < v[min]) min = j; } if (min != i) swap(v[i],v[min]); } end = clock(); cout << "select sort cost : " << end - start << endl; } /************************************************************************/ /* 归并排序 */ /************************************************************************/ void Merge(vector<int> &array,int start ,int mid ,int end) {//合并vector的start-mid和mid-end两段 int j,k,l; vector<int> temp(array); //引入temp,方便用array作为汇总 for( j=mid+1,k=start ; start<=mid&&j<=end ; k++ ) { if ( temp[start] < temp[j] ) array[k] = temp[start++]; else array[k] = temp[j++]; } if( start <= mid ) { for( l=0 ; l <= mid-start ; l++) array[k+l] = temp[start+l]; //将剩余0至min-start的复制到array } if( j <= end ) { for( l=0 ; l<=end-j ; l++ ) array[k+l]= temp[j+l]; //复制j至end到array } } void MergeSort( vector<int> &v, int low ,int high ) { if (low < high) { int mid = ( low + high )/2; //平分 MergeSort( v , low , mid ); //递归合并低段 MergeSort( v , mid+1 , high ); //递归合并高段 Merge( v , low , mid , high ) ; //合并排序后两段 } } //归并排序的测试函数 void merge_sort() { vector<int> v( orited ); double start ,end; start = clock(); MergeSort(v,0,MAX-1); end = clock(); cout << "merge sort cost : " << end - start << endl; } /************************************************************************/ /* 希尔排序 */ /************************************************************************/ const int SORTCOUNT = 20;//希尔排序的排序次数 const int dlta[SORTCOUNT] = {2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3};//增量数组 void ShellInsert(vector<int> &v , int dk) {//对v做一趟shell插入排序,增量为dk,不是1;j<=0时,找到插入位置 int i,j; for ( i=dk ; i < v.size() ; i++) { if (v[i] < v[i-dk]) { int temp = v[i];//暂存 for (j=i-dk ; j>0 && temp < v[j] ; j -= dk) v[j+dk] = v[j] ;//记录后移,查找插入位置 v[j+dk] = temp; //插入 } } } void ShellSort(vector<int> &v ,const int dlta[] ,int t) {//希尔排序 for (int k=0 ; k < t ; k++) ShellInsert(v,dlta[k]) ;//一趟增量为dlta[k]的插入排序 } void shell_sort() { vector<int> v( orited ); double start ,end; start = clock(); ShellSort(v,dlta,SORTCOUNT); end = clock(); cout << "shell sort cost : " << end - start << endl; } /************************************************************************/ /* 堆排序 */ /************************************************************************/ void HeapAdjust(vector<int> &v ,int s ,int m)//将堆用数组表示,此处为vector {//调整为大顶堆 int rc,j; rc = v[s]; for ( j=2*s ; j<= m ; j *= 2 ) {//沿key较大的孩子结点向下筛选 if (j<m && v[j] < v[j+1]) j++ ;//j为key较大的记录的下标 if ( rc >= v[j] ) break;// rc应插入在位置s上 v[s] = v[j] ; s = j; } v[s] = rc ; //插入 } void HeapSort(vector<int> &v) { // 对vector进行堆排序 int t , i; for( i=v.size()/2 ; i>0 ; i--) // 把v建成大顶堆 HeapAdjust( v,i,v.size()-1 ); for( i=v.size()-1 ; i>0 ; i-- ) {//将堆顶记录和当前未经排序子序列[0..i]中最后一个记录相互交换 t = v[0]; v[0] = v[i]; v[i] = t ; HeapAdjust(v,0,i-1); // 将v[0..i-1]重新调整为大顶堆 } } //堆排序测试函数 void heap_sort() { vector<int> v( orited ); double start ,end; start = clock(); HeapSort(v); end = clock(); cout << "heap sort cost : " << end - start << endl; } int main(int argc, char* argv[]) { randNum(); //每次更新MAX,重新生成随机数,存到vector vector_sort(); quick_sort(); bubble_sort(); select_sort(); merge_sort(); shell_sort(); heap_sort(); insert_sort(); return 0; }
关于sort(),这里 默认是按照升序排列的,如果想按照降序排列,可添加函数:
bool compare(int a ,int b) { return a>b;//a>b降序 a<b升序 }并调用时
sort(v.begin(),v.end(),compare);即可。
在程序的main部分给出了部分测试结果,其中shell的不具有太大参考性,于是没列(用书的原话说,shell排序的分析是一个复杂的问题,因为时间是所取增量序列的函数,这涉及一些数学上尚未解决的问题),将排序结果保存在文件中观看,可以看到,shell的结果,不像快速排序似的能完全正确,概率问题。
vc6测试结果如下:
|
Sort() |
Quick |
Bubble |
Select |
Merge |
Shell |
heap |
insert |
1k |
0 |
1 |
78 |
35 |
107 |
14 |
2 |
21 |
1w |
2 |
12 |
4883 |
3099 |
7947 |
1135 |
34 |
2274 |
10w |
26 |
142 |
410630 |
385299 |
774607 |
108289 |
267 |
220359 |
|
Sort() |
Quick |
Bubble |
Select |
Merge |
Shell |
heap |
1k |
26 |
2 |
193 |
291 |
15 |
90 |
8 |
1w |
302 |
29 |
12674 |
8203 |
216 |
3010 |
57 |
10w |
3624 |
378 |
1.02754e+006 |
804142 |
804142 |
804142 |
713 |
gcc下测试结果:
|
Sort() |
Quick |
Bubble |
Select |
Merge |
Shell |
heap |
insert |
1k |
0 |
0 |
9 |
3 |
1 |
2 |
1 |
2 |
1w |
2 |
3 |
725 |
452 |
81 |
170 |
4 |
294 |
10w |
29 |
27 |
55812 |
34269 |
4390 |
16272 |
45 |
28208 |
统计结果当然是明显的,除了在vs08中,sort()的确不是一般的快啊(尤其是vc6下,压倒性优势),紧跟其后的是quicksort和heapsort。对于heapsort,这里的堆,直接用顺序表示了,也算省了不少时间。
神奇的是vc6下的merge居然比冒泡还慢,没天理嘛,当然还好gcc这样一个标准的c++测试给了我们信心。
刚统计完发现插入没做,补充上统计结果, 目前就算是这样吧,有什么不对的,请跟帖说明修改啊。
后记:后来改用了双向冒泡,发现比原冒泡快了一倍多,比选择还略快(后来测试,起初的写法出错了,其实和单向冒泡速度是一样的)
代码如下:
for (i=0 ; i< MAX ; i++) { k = MAX-i-1; for (j=i+1 ; j< k ; j++)//改用双向冒泡 { if (v[j] < v[i]) { swap(&v[i],&v[j]); } if (v[MAX-j-1] > v[k]) { swap(v[MAX-j-1],v[k]); } } }
运行时间结果马上更新
本想把选择改为双向选择,min存低位,max存在高位,第二个循环j<MAX-i的,不过存在一些问题,比如可能交换2次的情况,在min循环交换后,又max交换,则不是预期的结果了,以后有心情再搞吧。