主要排序算法的比较及其优化
2017-09-29 18:38
274 查看
一,目的
本节内容主要讲了冒泡排序,插入排序,希尔排序,快速排序,并且提供了它们排序速度的比较,以及各自的优化。
二,主要排序算法
1,冒泡排序
冒泡排序又称起泡排序(bubble),之所以称为起泡排序,是因为每次排序关键字较小的元素像水中的气泡一样逐步向上漂浮,而关键字较大的元素像石块一样下沉。
请看下图:
从上图我们可以看到:第一次,挑出9,第二次排出8,第三次,第四次。。。是不是特别像鱼吐泡泡啊?小的元素慢慢地浮到上面了。
冒泡排序思路:将序列中的第一个元素与第二个进行比较,如果第一个比第二个大,则交换,再将第二个与第三个比,如果第二个不大于第三个元素则不用交换,直到第n-1个元素与第n个元素比较,这样每次找出最大的放到相应的位置上。第二次外层循环再在n-1个元素中找出最大的放到第n-1个位置,直到只剩下一个元素没有排为止。
举例:有数列 11,6,4 ,9 ,1,5,7,4
11比6大交换,当前为11,再与4进行比较,11大于4,交换,当前为11,第三次比较的数为9,11比9大,交换,所以继续找下一个数。
代码实现:
其中arry是待排数组的指针,N为数组长度,swap用来交换两个数。通过分析可得,冒泡排序的时间复杂度为:O(N*N),并且是稳定排序,因为,只有当比较的元素比当前元素小时才会进行交换,所以相等的两个数是不会产生交换行为的。
1.1 冒泡排序的优化。
冒泡排序的关键便是与后面的元素进行比较,然后进行交换,但是当我们发现某次排序的过程中都没有发生过交换的行为,那么便可以认为,剩下的元素都是有序的,所以也就不用再比较下去。
例如数列:1,2,3,4,5,6,7,8,9;
进行第一次排序我们发现后面的数列2大于1,3大于2,4大于3。。。9大于8就没有进行交换的行为,此时便可以认为是有序的,而不用再进行比较
1.2 优化代码实现。
思路:每次进行内部循环,设置个flag用来标记此次循环有没有发生交换。
2,快速排序
在实际运用中冒泡排序使用的频率及其低,因为每次排序它都要经过大量的交换,十分费时间,但在它的基础上实现的快速排序,却有着很好的效率。顾名思义。
快速排序的实现使用了分治的思想:
1,找一个基准值(通常情况下选择待排序列中的第一个或最后一个元素)
2,将序列中比基准值大的移到其右边,比它小的移到它的左边
3,将基准值左边的新序列和右边的新序列分别进行1,2步,直到待排序列元素只剩下一个。
在实现的过程中用到了递归。
举例说明:5,4,1,8 ,10,7,9,2,4,3(刚开始)
基准值选:5
进行比较:(3,4,2,1,4)5(7,9,10,8)
基准值选3,7
分别为:(2,1)3(4,4)
7(9,10,8)
以此进行下去,当划分为一个元素时便终止,则整个待排序列都为有序的了,快排平均时间复杂度O(N*logN)
2.1 代码实现
快排平均时间复杂度为O(N*logN),但当基准值选择不当便会较低到O(N*N);
这是因为,当基准值选取恰好为当前序列中最小的时候,便意味着每次排序都只是对1和N-1个数进行排序,则效率将大大降低,例如:
数列:1,7,8,9,4,2,5,3;
当选取1为基准值,你会发现没有比1小的,则需要比较n-1次,则对N个数进行排序大概需要O(N*N);
另一种情况:当待排序列为有序时。
改良快排的这种情况,主要有两个方法:
1,随机选取基准值法
2,三数取中法(将序列中的第一个,中间那个,最后一个进行大小比较,挑出排在中间的那个数为基准值)
现在我主要针对随机选取基准值法进行说明,每趟快排的时候我们可以使用随机数法选择基准值,这样大大降低了基准值挑选不当,以及待排序列为有序的情况。
代码如下:
3,插入排序及希尔排序在我的博客中已经有详细的介绍,所以现在不在进行详述。
三,排序效率的比较
请直接看结果:
上图是每种情况的测试时间,具体单位其实并不重要,这只是用来进行比较的一种策略。
第一行:
测试1千个数据从小到大排列所需的时间,数据用随机数产生(每种排序算法排的随机数都是相同的,下同),范围:【0,10000)
第二行:
测试1万个数据从小到大排列所需时间,范围【0,10000);
第三行:
测试1千个数据从小到大排列所需时间,范围【0,1000);
第四行:
测试1千个数据从小到大排列所需时间,范围【0,500);
第五行:
测试1千个数据从小到大排列所需时间,范围 【0,10000);
第六行
测试2千个已排好序列排序所用时间,范围【0,100000);
注:以上测试结果还与测试所用的机型,测试的方法有关,并且所有类型的测试我都只测试了一遍,按要求应该测多次,然后求平均数来计量的。
四,总结
1,经过测试我们发现在大部分情况下快速排序的效率都是极其高的,但细心的同学可能发现当测试的数据为有序的时候,快排的效率便会降低,并且有可能造成栈溢出等问题。
2,插入排序整体上比冒泡排序和希尔排序的效率高
3,冒泡排序速度最慢。
4,工作生活中有好多地方都设计排序的问题,学好排序的相关知识很有用。
本节内容主要讲了冒泡排序,插入排序,希尔排序,快速排序,并且提供了它们排序速度的比较,以及各自的优化。
二,主要排序算法
1,冒泡排序
冒泡排序又称起泡排序(bubble),之所以称为起泡排序,是因为每次排序关键字较小的元素像水中的气泡一样逐步向上漂浮,而关键字较大的元素像石块一样下沉。
请看下图:
从上图我们可以看到:第一次,挑出9,第二次排出8,第三次,第四次。。。是不是特别像鱼吐泡泡啊?小的元素慢慢地浮到上面了。
冒泡排序思路:将序列中的第一个元素与第二个进行比较,如果第一个比第二个大,则交换,再将第二个与第三个比,如果第二个不大于第三个元素则不用交换,直到第n-1个元素与第n个元素比较,这样每次找出最大的放到相应的位置上。第二次外层循环再在n-1个元素中找出最大的放到第n-1个位置,直到只剩下一个元素没有排为止。
举例:有数列 11,6,4 ,9 ,1,5,7,4
11比6大交换,当前为11,再与4进行比较,11大于4,交换,当前为11,第三次比较的数为9,11比9大,交换,所以继续找下一个数。
代码实现:
void bubble_sort(int* arry, int n) { for(int i=n-1;i>0;i--) //外循环控制次数 { for(int j=0;j<i;j++) //内循环进行比较 { if(arry[j]>arry[j+1]) swap(arry[j],arry[j+1]); } } }
其中arry是待排数组的指针,N为数组长度,swap用来交换两个数。通过分析可得,冒泡排序的时间复杂度为:O(N*N),并且是稳定排序,因为,只有当比较的元素比当前元素小时才会进行交换,所以相等的两个数是不会产生交换行为的。
1.1 冒泡排序的优化。
冒泡排序的关键便是与后面的元素进行比较,然后进行交换,但是当我们发现某次排序的过程中都没有发生过交换的行为,那么便可以认为,剩下的元素都是有序的,所以也就不用再比较下去。
例如数列:1,2,3,4,5,6,7,8,9;
进行第一次排序我们发现后面的数列2大于1,3大于2,4大于3。。。9大于8就没有进行交换的行为,此时便可以认为是有序的,而不用再进行比较
1.2 优化代码实现。
思路:每次进行内部循环,设置个flag用来标记此次循环有没有发生交换。
void bubble_sort(int* arry, int n) { for (int i = n-1; i >0; i--) //循环n-1次 { bool flag = false; for (int j = 0; j<i; j++) { if (arry[j]>arry[j+1]) { swap(arry[j], arry[j+1]); flag = true; //有交换置标志位为真 } } printArry(arry, n); if (flag == false) break; } }
2,快速排序
在实际运用中冒泡排序使用的频率及其低,因为每次排序它都要经过大量的交换,十分费时间,但在它的基础上实现的快速排序,却有着很好的效率。顾名思义。
快速排序的实现使用了分治的思想:
1,找一个基准值(通常情况下选择待排序列中的第一个或最后一个元素)
2,将序列中比基准值大的移到其右边,比它小的移到它的左边
3,将基准值左边的新序列和右边的新序列分别进行1,2步,直到待排序列元素只剩下一个。
在实现的过程中用到了递归。
举例说明:5,4,1,8 ,10,7,9,2,4,3(刚开始)
基准值选:5
进行比较:(3,4,2,1,4)5(7,9,10,8)
基准值选3,7
分别为:(2,1)3(4,4)
7(9,10,8)
以此进行下去,当划分为一个元素时便终止,则整个待排序列都为有序的了,快排平均时间复杂度O(N*logN)
2.1 代码实现
void quick_sort(int* arry,int size,int begin, int end) { if (begin>=end) //结束条件 return; int i = begin, j = end; int temp = arry[i]; //基准值的选取 while(i < j) { while (j> i&& arry[j] > temp) { j--; } if (j>i) //当比基准值小时 { swap(arry[i], arry[j]); //交换 i++; } while (i<j && arry[i] <=temp) { i++; } if (i<j) //当比基准值大时 { swap(arry[j], arry[i]); j--; } } arry[i] = temp; quick_sort(arry, size, begin, i - 1); //递归进行 quick_sort(arry, size, i + 1, end); }
快排平均时间复杂度为O(N*logN),但当基准值选择不当便会较低到O(N*N);
这是因为,当基准值选取恰好为当前序列中最小的时候,便意味着每次排序都只是对1和N-1个数进行排序,则效率将大大降低,例如:
数列:1,7,8,9,4,2,5,3;
当选取1为基准值,你会发现没有比1小的,则需要比较n-1次,则对N个数进行排序大概需要O(N*N);
另一种情况:当待排序列为有序时。
改良快排的这种情况,主要有两个方法:
1,随机选取基准值法
2,三数取中法(将序列中的第一个,中间那个,最后一个进行大小比较,挑出排在中间的那个数为基准值)
现在我主要针对随机选取基准值法进行说明,每趟快排的时候我们可以使用随机数法选择基准值,这样大大降低了基准值挑选不当,以及待排序列为有序的情况。
代码如下:
int getRand(int i, int j) //产生[i,j)的随机数 { srand((unsigned)time(NULL)); int x = rand() % (j - i) + i; return x; }
void quick_sort_practice(int* arry,int size,int begin,int end) { assert(arry != NULL); if (begin >= end) //结束条件 return; int i = begin, j=end; int pos = getRand(i, j); //产生基准值 swap(arry[i], arry[pos]); int temp = arry[i]; //以左边的元素为基准值 while (i < j) { while (j > i && arry[j] > temp) { j--; } if (j > i) { swap(arry[i], arry[j]); i++; } while (i < j && arry[i] <= temp) { i++; } if (i < j) { swap(arry[j], arry[i]); j--; } } arry[i] = temp; quick_sort_practice(arry, size, begin, i - 1); quick_sort_practice(arry, size, i + 1, end); }
3,插入排序及希尔排序在我的博客中已经有详细的介绍,所以现在不在进行详述。
三,排序效率的比较
请直接看结果:
上图是每种情况的测试时间,具体单位其实并不重要,这只是用来进行比较的一种策略。
第一行:
测试1千个数据从小到大排列所需的时间,数据用随机数产生(每种排序算法排的随机数都是相同的,下同),范围:【0,10000)
第二行:
测试1万个数据从小到大排列所需时间,范围【0,10000);
第三行:
测试1千个数据从小到大排列所需时间,范围【0,1000);
第四行:
测试1千个数据从小到大排列所需时间,范围【0,500);
第五行:
测试1千个数据从小到大排列所需时间,范围 【0,10000);
第六行
测试2千个已排好序列排序所用时间,范围【0,100000);
注:以上测试结果还与测试所用的机型,测试的方法有关,并且所有类型的测试我都只测试了一遍,按要求应该测多次,然后求平均数来计量的。
四,总结
1,经过测试我们发现在大部分情况下快速排序的效率都是极其高的,但细心的同学可能发现当测试的数据为有序的时候,快排的效率便会降低,并且有可能造成栈溢出等问题。
2,插入排序整体上比冒泡排序和希尔排序的效率高
3,冒泡排序速度最慢。
4,工作生活中有好多地方都设计排序的问题,学好排序的相关知识很有用。
相关文章推荐
- 排序算法—比较排序之插入排序及其优化
- 排序算法的比较、选择及其改进
- 非比较排序算法之计数排序及其实现
- 各种排序算法的实现及其比较
- 排序算法(四)冒泡排序及其优化
- 各种排序算法的实现及其比较
- 常用的八种排序算法的原理实现及其比较(JAVA实现)
- 排序算法及其比较--数据结构课设
- 各种排序算法的实现及其比较
- 各种排序算法的实现及其比较(c++实现)
- 各种排序算法的实现及其比较
- 各种排序算法的实现及其比较
- 6种排序算法及其比较 简单选择排序,堆排序,简单插入排序,希尔排序,冒泡排序,快速排序,归并排序
- 各种排序算法的实现及其比较(c++实现)
- PHP排序算法之快速排序(Quick Sort)及其优化算法详解
- 几个简单的排序算法及其优化
- 排序算法-------冒泡排序及其优化
- 数组第K小数问题 及其对于 快排和堆排 的相关优化比较
- 三种初级排序算法(冒泡、选择、直接插入)java实现及其性能比较
- 几个排序算法及其性能比较