测试代码文件 ----注释为数据时间结论
1 #include"pch.h" 2 #include"sort.h" 3 #include <iostream> 4 #include<ctime> 5 #include<random> 6 #include<algorithm> 7 #include<functional> 8 #include<iomanip> 9 using namespace std; 10 bool test() 11 { 12 13 14 const int num = 10000; 15 int ori[num]; 16 int answer[num]; 17 for (int i = 0; i < num; i++) 18 { 19 ori[i] = rand() % 1000; 20 answer[i] = ori[i]; 21 } 22 /*cout << "原队列: "; 23 for (auto i : ori) 24 cout << i << ‘ ‘; 25 cout << endl;*/ 26 27 std::sort(answer, answer + num); 28 /*cout << "正确队: "; 29 for (auto i : answer) 30 cout << i << ‘ ‘; 31 cout << endl;*/ 32 33 34 //quicksort(ori,sizeof(ori)/sizeof(int)); //2s 35 //selectsort(ori, sizeof(ori) / sizeof(int)); //14s 比想象中差 36 //mergesort(ori, sizeof(ori) / sizeof(int)); //2s~3s 37 //insertsort(ori, sizeof(ori) / sizeof(int)); //8s 38 //bubblesort(ori, sizeof(ori) / sizeof(int)); //29s 大数组下效率奇差 39 //hsort(ori, sizeof(ori) / sizeof(int)); //2s 40 heapsort(ori, sizeof(ori) / sizeof(int)); //3~4s由于运行环境不同,开的程序多了, 41 //且加了接口,没有直接映射故应该跟merge一个档次
//以前测试过,heapsort比较交换次数比直接insert小很多
42 43 /*cout << "后队列: "; 44 for (auto i : ori) 45 cout << i << ‘ ‘; 46 cout << endl;*/ 47 48 //校对 49 bool a = true; 50 for (int i = 0; i < num; i++) 51 { 52 if (ori[i] != answer[i]) 53 { 54 a = false; 55 break; 56 } 57 } 58 if (!a) 59 { 60 exception e("排序错误"); 61 throw e; 62 } 63 return a; 64 65 } 66 void testSortWithTime() 67 { 68 69 clock_t start, finish; 70 start = clock(); 71 //随机数种子为定值,保证了各个排序的所有测试数据都是一样的 72 srand(time(NULL)); 73 int i = 100; 74 try { 75 while (--i) 76 { 77 78 test(); 79 } 80 } 81 catch (exception e) 82 { 83 cout << e.what() << endl; 84 } 85 finish = clock(); 86 double total = (finish - start) / CLOCKS_PER_SEC; 87 if (!i) 88 cout << "程序正确执行 " << endl; 89 else 90 { 91 cout << "程序未能正确执行 " << endl; 92 } 93 cout << "耗时\t" << total << " second" << endl; 94 return; 95 }
通用头文件
1 #pragma once 2 3 void selectsort(int *p, int num); 4 5 void Qsort(int *p, int low, int high); 6 void quicksort(int * p, int count); 7 8 9 void msort(int *p, int head1, int tail1, int head2, int tail2); 10 void mesort(int *p, int head, int tail); 11 void mergesort(int *p, int num); 12 13 void insertsort(int *p, int num); 14 15 void bubblesort(int *p, int num); 16 17 18 void hsort(int *p,int num); 19 void heapsort(int*p,int num); 20 21 //测试函数 22 bool test(); 23 void testSortWithTime();
1.插入排序
1 #include"pch.h" 2 #include"sort.h" 3 void insertsort(int *p, int num) 4 { 5 if (p == nullptr || num < 2)return; 6 7 8 for (int i = 1; i < num; i++) 9 { 10 int temp = p[i]; 11 int j = i ; 12 for (; j > 0; ) 13 { 14 15 //必须用原来的数值,也就是temp进行比较,而非p【j】 16 if (temp < p[j - 1]) 17 { 18 p[j] = p[j - 1]; 19 --j; 20 } 21 else break; 22 } 23 if (j != i) 24 { 25 p[j] = temp; 26 } 27 } 28 }
2.冒泡排序-------每次排序就是一个最重(大)的元素下落,与其说冒泡,数组大时效率极低(即使每轮有结束的机会),不如说是低端的选择排序,
好歹选择排序(同样是选一个元素,不过是选择最小的罢了,挡板在前)才交换一次,冒泡排序交换多次,挡板移动
1 #include"pch.h" 2 #include"sort.h" 3 void bubblesort(int *p, int num) 4 { 5 int temp; 6 int time; 7 8 //i为次轮将要排序的挡板,即次轮排序结束后,i不参与下轮比较 9 for (int i = num - 1; i > 0; --i) 10 { 11 time = 0; 12 //j<=i 即i此论是要比较的 13 for (int j = 1; j <=i; j++) 14 { 15 if (p[j] < p[j - 1]) 16 { 17 temp = p[j]; 18 p[j] = p[j - 1]; 19 p[j - 1] = temp; 20 ++time; 21 22 } 23 24 } 25 if (!time)break; 26 } 27 }
3.选择排序--------------------------挡板在前,没回选一个最小的固定其位置,然后移动挡板,与冒泡排序不同的地方,可能就输在数组大致有序时,
冒泡可以强行结束(冒泡排序在完全反序时效率感人肺腑)
1 #include"pch.h" 2 #include"sort.h" 3 4 void selectsort(int *p, int num) 5 { 6 7 int temp, min; 8 for (int i = 0; i < num - 1; i++) 9 { 10 min = i; 11 for (int j = i + 1; j < num; j++) 12 { 13 if (p[min] > p[j]) 14 min = j; 15 } 16 if (min != i) 17 { 18 temp = p[min]; 19 p[min] = p[i]; 20 p[i] = temp; 21 } 22 23 } 24 }
4.希尔排序
原理可以视为多趟的插入排序,与插入排序不同的是,插入排序一次元素移动只会减少一点无序度(因为其是相邻交换),而希尔排序可能
会降低多点无序度(因为跨度不一样,也因为跨度不一样,所以其是不稳定排序的),每次希尔排序都选择一个跨度,直至为1则停止。希尔排序skip数组的要求是
逐个降低,且出1意外,任意俩个数的乘积不等于第三个数,最好连最大公因数都维持为1,减少没必要的排序。
1.选择跨度进行单跨度为skip的插入排序
2.单跨度插入排序,跨度为skip
2.1)选择开始元素start,进行从0开始,到跨度大小即结束(不要等于大于skip,因为没有意义,重复排序了,如选择了start=skip+1,那么选择start=1完成的时候,start=skip+1就已经是有序的)
2.2)选择start后,注意skip的大小进行跨步递增,也要注意跨步递减(防止越界为负数,这跟插入排序是一样的,只不过插入排序默认0为边界,而希尔排序的每次插入排序是动态边界),
3.但跨度的插入排序后,选择下一个skip,进行排序,直至结束
PS:希尔排序的效率很大程度上取决于skip数组,必须递减(否则没意义),无最小公因数,以减少无意义比较,其为不稳定排序,某种程度上说,乱序越能体现其速度
1 #include"pch.h" 2 #include"sort.h" 3 void hsort(int *p, int num) { 4 //跨步数组必须有序,且最后必须为1,最好使得数组内除了1以外的成员间 5 //任意俩者的乘积不等于第三者 6 if (p == nullptr)return; 7 int skip_arry[] = { 1,2,3,5,7,11 }; 8 int size = sizeof(skip_arry) / sizeof(int); 9 int skip; 10 for (int i=size-1;i>=0;--i) 11 { 12 skip = skip_arry[i]; 13 //检测跨步,进行剪枝 14 if (skip >= num)continue; 15 16 //size趟跨步为skip的插入排序 17 //j<k的原因是,当j=skip时,以0开头的skip跨步的结果, 18 //就已经使得以skip开头skip跨步的序列变成有序,故没必要 19 20 21 //以下进行的是以j为起点,跨步为skip的插入排序 22 for (int j = 0; j < skip ; j++) 23 { 24 25 // 26 for (int k=skip+j; k < num; k += skip) 27 { 28 int temp = p[k]; 29 int h = k; 30 //懒得用for,while比较干脆 31 //h不可能小于j,等于j时立刻停止 32 //一般插入排序将j作为0的原因是,0就是边界,这时需要调整 33 while (h > j) 34 { 35 if (temp < p[h - skip]) 36 { 37 p[h] = p[h - skip]; 38 h -= skip; 39 } 40 else break; 41 42 }//end while 43 if (h != k) //插入的元素位置发生了变换 44 { 45 p[h] = temp; 46 } 47 }//end 以下进行的是以j为起点,跨步为skip的插入排序 48 49 }//end 跨步skip_array[i] 50 51 }//end sort 52 }
5.归并排序
new俩个数组,递归对其使用归并,直至这俩个数组是有序时返回;然后合并,非长简单,且是稳定排序(只要传入的头一个位置在相等时优先插入有序队列即可),但需要一定的内存空间
理论上下面的算法实现可以进行优化,下面的算法实现是new左右俩边数组大小后再进行递归调用,排序归并使得其有序,
以下代码实际上在最大占用时,占用了约2倍原数组内存空间用以存数(1+0.5+0.25+0.125+........),而实际上可以减少到1倍的原数组内存空间占用(0.5+0.25+0.125+0.0625.....),
因为其是分步的,左边有序化时,右边并没有卵用,可以不new,减少最大内存占用峰值
为先new左边数组大小,调用递归,使其有序----new右边数组大小,调用递归,使得右边有序-----------归并俩个数组,使其有序
1 #include"pch.h" 2 #include"sort.h" 3 /**********************************************************************************************************************************************************************/ 4 5 /* 6 归并排序主要是有序序列的合并和无序数列的二分递归 7 传递实际下标,有利于直接进行逻辑思考 8 使用双链表,浪费了多一个指针,但是不用下标计算来计算去,考虑越界也比较简单 9 */ 10 11 //采用实际下标进行传值 12 void msort(int *p, int head1, int tail1, int head2, int tail2) 13 { 14 int x = tail1 - head1 + 1; 15 int y = tail2 - head2 + 1; 16 int *p1 = new int[x]; 17 int *p2 = new int[y]; 18 for (int i = 0; i < x; i++) 19 { 20 p1[i] = p[head1 + i]; 21 } 22 for (int j = 0; j < y; j++) 23 { 24 p2[j] = p[head2 + j]; 25 } 26 27 int i = 0; 28 int j = 0; 29 int k = head1; 30 31 32 while ((i < x) && (j < y)) 33 { 34 if (p1[i] < p2[j]) 35 { 36 p[k] = p1[i]; 37 ++i; 38 ++k; 39 } 40 else 41 { 42 p[k] = p2[j]; 43 ++j; 44 ++k; 45 } 46 } 47 48 49 //归并余下 50 if (i == x) 51 { 52 for (; j < y;) 53 { 54 p[k] = p2[j]; 55 ++k; 56 ++j; 57 } 58 } 59 else 60 { 61 for (; i < x;) 62 { 63 p[k] = p1[i]; 64 ++k; 65 ++i; 66 } 67 68 } 69 delete[]p1; 70 delete[]p2; 71 72 } 73 74 //对一段数列进行排序并归并 75 void mesort(int *p, int head, int tail) 76 { 77 if (head >= tail)return; 78 79 //防止越界 80 int mid = head + (tail - head) / 2; 81 82 83 mesort(p, head, mid); 84 mesort(p, mid + 1, tail); 85 msort(p, head, mid, mid + 1, tail); 86 87 } 88 89 90 // 用做排序统一接口 91 void mergesort(int *p, int num) 92 { 93 mesort(p, 0, num - 1); 94 }
6.堆排序
以下排序习惯了不用0元素作为开始元素,因为这样方便计算,用0元素要进行相应的二叉树对应子树映射,非常麻烦,故如果实在是0地址有元素,则申请内存进行简单搬移搞定
当然也可以用脑子去映射(我大概没有这玩意),也可以黑科技,不对0位置的元素进行排序,排序完成后1-n是有序的,进行一次插入排序,移动0位置的元素即可(最恶劣情况可能移动n次)
1 #include"pch.h" 2 #include"sort.h" 3 4 void adjust(int *p, int place, int num) 5 { 6 if (p == nullptr)return; 7 int left = 2 * place; 8 if ( left > num)return; 9 int righ = left + 1; 10 if (righ > num) 11 { 12 if (p[place] < p[left]) 13 { 14 int temp = p[place]; 15 p[place] = p[left]; 16 p[left] = temp; 17 adjust(p,left,num); 18 } 19 return; 20 } 21 else 22 { 23 int max = (p[left]>p[righ]?left:righ); 24 if (p[place] < p[max]) 25 { 26 int temp=p[place]; 27 p[place] = p[max]; 28 p[max] = temp; 29 adjust(p,max,num); 30 } 31 return; 32 } 33 34 } 35 void realheapsort(int *p,int num) 36 { 37 //为了方便计算,这里是num实际上是表示数组的最大下标; 38 //且以下标1开始 39 //just a interface 40 41 int mid = (1 + num) / 2; 42 for (int i = mid; i >= 1; --i) 43 { 44 adjust(p, i, num); 45 } 46 int temp; 47 while (num > 1) 48 { 49 temp = p[num]; 50 p[num] = p[1]; 51 p[1] = temp; 52 --num; 53 adjust(p, 1, num); 54 } 55 return; 56 } 57 void heapsort(int *p, int num) 58 { 59 int *pp = new int[num + 1]; 60 for (int i = 0; i < num; i++) 61 { 62 pp[i + 1] = p[i]; 63 } 64 realheapsort(pp, num); 65 for (int i = 0; i < num; i++) 66 { 67 p[i] = pp[i+1]; 68 } 69 delete[]pp; 70 return; 71 72 73 }
7.快速排序
快速排序排序后,浮标元素左边是恒小于等于他,右边恒大于等于他,其效率很大程度取决与一开始的浮标元素选取,如果是在待排序数组内位于中间值,那么就能减少排序次数,
而下面的代码为了省事,直接选第一个作为浮标,然后直接从后面进行排序。可以多值取中后(任选多个值,取得一个中间值),将其与第一位元素交换,在进行快速排序,减少比较交换次数
由于 浮标元素左边是恒小于等于他,右边恒大于等于他 ,那么浮标经过一次快速排序过程后,其在整个序列中就是“”相对位置有序的“”,因此,其下标n就是代表其整个序列的第n+1位元素,
可以用来在算法题中快速得出第n位元素
1 #include "pch.h" 2 #include"sort.h" 3 4 void Qsort(int *p, int low, int high) 5 { 6 if (low >= high)return; 7 int point = low; 8 int temp = p[point]; 9 int nlow = low; 10 int nhigh = high; 11 while (low < high) 12 { 13 14 while (low < high) 15 { 16 //必须设置为>=不然,在重复序列中,其会无限循环 17 // p[mid]=p[low]=p[high] ,三个无限互换 18 if (p[high] >= temp) 19 --high; 20 else break; 21 } 22 p[point] = p[high]; 23 point = high; 24 25 26 while (low < high) 27 { 28 if (p[low] <= temp) 29 ++low; 30 else break; 31 } 32 p[point] = p[low]; 33 point = low; 34 35 36 } 37 p[point] = temp; 38 Qsort(p, nlow, point - 1); 39 Qsort(p, point + 1, nhigh); 40 41 42 } 43 44 void quicksort(int * p, int count) 45 { 46 Qsort(p, 0, count - 1); 47 }