常用的排序算法
来源:互联网 发布:苹果非编软件 编辑:程序博客网 时间:2024/05/16 10:21
排序算法有很多,是一个很杂但是很重要的知识点,复习一下:
笼统地来说,排序分为内部排序(Internal Sorting)和外部排序(External Sorting),我们讨论的一般是内部排序。从实现方式上讲,排序又可以分为比较排序和非比较排序,前者的时间复杂度在O(nlogn)——O(n^2)之间,后者均为O(n),在文章末尾有关于各种排序算法时间复杂度、空间复杂度、是否稳定等性质的总结。
经典的排序算法主要有以下几种,其中1——7属于比较排序,8、9是非比较排序。
1、冒泡排序(Bubble Sort)
(1)原汁原味的冒泡。。
void bubble_sort(int a[],int n){int i,j,tmp;for(i=0;i<n-1;i++){//n个元素需要排序n-1趟for(j=0;j<n-i-1;j++){//每做一次外循环就有i+1个元素归位(注意i从0开始,所以是i+1),那么就只需要排剩下的n-i-1个if(a[j]>a[j+1]){tmp=a[j+1];a[j+1]=a[j];a[j]=tmp;}}}}
对于n个元素只需要n-1趟,每趟都让一个元素归位,大的或者小的元素就像冒泡泡一样跑到前边或者后边去了。。。
(2)改进版的冒泡——鸡尾酒排序(Cocktail Sort)
这家伙其实就是两趟冒泡排序,看一个例子(假设按升序排列,蓝色加粗的表示每次排好不用再管的元素):
比如9 8 4 12 7 2 5
第一趟先从左到右找到最大的,是12,然后把12扔到最后去:
9 8 4 7 2 5 12
然后再从右往左扫回来(不再管12了),找到最小的2,扔前面:
2 9 8 4 7 5 12
再从左往右(不再管2和12),找到了9,扔到最后:
2 8 4 7 5 9 12
再从右往左,这回是4:
2 4 8 7 5 9 12
如此往复。。。
就得到答案了:2 4 5 7 8 9 12
这个过程就特别像调鸡尾酒啊。。。左左右右来来回回摇来摇去的=。=所以这个排序算法还有一堆很萌的名字:比如“Shaker Sort”,“Happy Hour Sort”之类的=。=(难不成命名的人觉得这个过程很欢乐=。=?)
代码实现:
void cocktail_sort(int a[],int n){int low=0,high=n-1;int index=low;while(high>low){for(int i=low;i<high;i++){if(a[i]>a[i+1]){swap(a[i],a[i+1]);index=i;}}high=index;for(int i=high;i>low;i--){if(a[i]<a[i-1]){swap(a[i],a[i-1]);index=i;}}low=index;}}鸡尾酒排序的代码还是很好理解的,每趟用一个index来回记录高低位即可。
2、插入排序(Insertion Sort)
是最简单的排序之一:
#include<iostream>using namespace std;void insertion_sort(int a[],int n){int i,j,tmp;for(i=1;i<n;i++){tmp=a[i];for(j=i;j>0&&a[j-1]>tmp;j--)a[j]=a[j-1];a[j]=tmp;}}int main(){int a[9]={1,9,0,45,88,3,5,7,100};insertion_sort(a,9);for(int i=0;i<9;i++)cout<<a[i]<<" ";return 0;}运行效果截图:
插入排序的思想是:每一次排序都严格保证位置i之前的元素都比i处小,在排序的过程中只要发现比它大的,就让这个更大的元素向前覆盖,比如:
1 5 7 9 10 3
现在排到了3这里,也就是tmp=3,需要检查3前面的,发现10比它大,于是10向前覆盖:
1 5 7 9 10 10
再看9,9仍然大于3,也要向前覆盖:
1 5 7 9 9 10
再看7,同理:
1 5 7 7 9 10
再看5:
1 5 5 7 9 10
最后到1,发现1小于3,于是执行a[j]=tmp,3占据a[1],得到:
1 3 5 7 9 10
3、希尔排序(Shell Sort)
是一个叫做Donald Shell的计算机科学家发明的排序算法,也是最早突破二次时间屏障的第一批算法之一,可以看成是insertion sort的加强版,查了一下,Shell在2015年11月2日逝世了,享年91岁,对大牛的逝去表示哀悼。。。
其实算法思想并不复杂,例如对一堆待排序的数:9 8 3 5 12 0 7 2 6 88 21
我们把它按照所谓的“增量序列”来分组,常用的序列是二分的,这也是希尔本人的序列,即按照n/2,n/4,...这样进行下去(除不尽就向下取整,例如9/2这里其实是floor(9/2)=4),这个序列叫做“Shell's increment sequence”,除此之外还有Hibbard's increment sequence, Sedgewick's increment sequence等。。(后尤其是面这位Sedgewick学过算法的同学应该都不陌生。。Knuth的徒弟,红黑树就是因为他和另一位叫Leonidas John Guibas的计算机科学家而得名的orz)不同的序列对算法性能有很大影响,其中的分析比较复杂,在此不表。这里为了简便起见,选择的是Shell’s increment sequence。
回到上面的例子,9 8 3 5 12 0 7 2 6 88 21的长度是11,那么就按照5,2,1的间隔来分组(下面的例子参考了CY老师的课件,我认为她是明白这个思想的。。。但是过程和后面的代码对不上号,所以有点误导人。。。这个过程不看也罢,可以直接看后面的代码和代码后我给的说明):
间隔5的为一组(以相同颜色区分):
9 8 3512 072 68821
然后排序:
9 7 2 5 12 0 8 3 6 88 21
再按照2的间隔来分组:
0 8 3 5 12 9 726 88 21
然后排序:
0 2 3 5 12 8 7 9 6 88 21
最后间隔为1的,也就是全部序列,排序,然后就得到结果了:
0 2 3 5 6 7 8 912 21 88
代码(和上面的过程对不上号,但是是《Data Structure and Algorithm Analysis》(Weiss著)一书给的例程):
void shell_sort(int a[],int n){int i,j,increment;int tmp;for(increment=n/2;increment>0;increment/=2){for(i=increment;i<n;i++){//内部其实是选择排序(Insertion Sort)tmp=a[i];for(j=i;j>=increment;j-=increment){if(tmp<a[j-increment])a[j]=a[j-increment];elsebreak;}a[j]=tmp;}}}按照代码,执行过程是:
对于9 8 3 5 12 0 7 2 6 88 21,一开始的增量是5,i=5
我们先找到第5个元素,再回过头去看0,然后i=6,看6-increment,即1,再然后是i=7,看2,i=8,看3......这样一直下去,所以分组应该是:
9,0
7,8
2,3
6,5
88,12
21,0
下一次增量是2,i=2...仿上...
所以CY姥姥给的那个过程看看算了。。。别太当真=。=
4、选择排序(Selection Sort)
我总是把它的名字和插入排序混淆orz
选择排序的思想是:从第一个元素开始扫描,选出这堆数中最小(最大)的那个,与第一个元素交换,如此循环往复。例如:
8 9 5 1 3 11
从8开始,找到了最小的1,交换:
1 9 5 8 3 11
从9开始,找到了最小的3,交换:
1 3 5 8 9 11
从5开始,一找发现它自己就是最小的,那就乖乖呆着不交换了=。=(这个地方在代码里也有体现)
1 3 5 8 9 11
如此循环往复,把后面的数都检查完就得到:
1 3 5 8 9 11
代码很简单:
void selection_sort(int a[],int n){int i,j,min;for(i=0;i<n;i++){min=i;//假设当前元素是最小的,min记录它的下标for(j=i+1;j<n;j++){if(a[j]<a[min])min=j;}if(min!=i)//如果这个元素本身就是最小的,就不要交换了swap(a[i],a[min]);}}
5、堆排序(Heap Sort)
这是一个很重要的内容,想把它和堆放到一起写,所以这里先略。
6、归并排序(Merge Sort)
归并排序的算法思想是divide and conquer,这个东西和外部排序有很大的联系。。。(外部排序用的就是归并的思想)算法可以递归实现,也可以非递归实现,先来通过例子
看看这个过程:
比如有两个数组:2 4 7 9和0 11 7
首先指针p1指到2,p2指到0,2和0比,0小,把0放进储存结果的数组,p2移动到11,11和2比,2小,把2放进储存结果的数组,p1移到4......如此往复
代码:
/*去年复习照着书用C写的。。。*/void MergeSort(int A[],int N){//MergeSort的作用就是去调用MSort//TmpArray用来暂时存放结果int * TmpArray;TmpArray=(int *)malloc(sizeof(int)*N);if(TmpArray!=NULL){MSort(A,TmpArray,0,N-1);free(TmpArray);}else{printf("Fatal Error!No spcace for TmpArray!\n");exit(0);}}void MSort(int A[],int TmpArray[],int Left,int Right){int center;if(Left<Right){center=(Right+Left)/2;MSort(A,TmpArray,0,center);MSort(A,TmpArray,center+1,Right);Merge(A,TmpArray,Left,center+1,Right);}}void Merge(int A[],int TmpArray[],int Lpos,int Rpos,int RightEnd){int i,LeftEnd,NumElements,TmpPos;LeftEnd=Rpos-1;//左边的终止位置=右边的开始位置-1NumElements=RightEnd-Lpos+1;//需要排序的个数=右边终止位置-左边起始位置+1TmpPos=Lpos;while(Lpos<=LeftEnd&&Rpos<=RightEnd)if(A[Lpos]<=A[Rpos])TmpArray[TmpPos++]=A[Lpos++];elseTmpArray[TmpPos++]=A[Rpos++];while(Lpos<=LeftEnd)TmpArray[TmpPos++]=A[Lpos++];while(Rpos<=RightEnd)TmpArray[TmpPos++]=A[Rpos++];for(i=0;i<NumElements;i++,RightEnd--)A[RightEnd]=TmpArray[RightEnd];}
例程一共有3个函数:
第一个遵守排序函数的定义,写成voidxxxsort(Type A[],int N),里面定义了一个数组,用来存放排序结果。
第二个是递归函数,从0排到中间,从中间+1排到右边,再调用排序函数Merge。这个函数有四个参数:原数组,存放结果的数组,确定范围的左、右。
第三个是具体过程的实现,有五个参数:原数组,存放结果的数组,左端第一个位置,右端第一个位置和最末位。
7、快速排序(Quick Sort)
肥肠重要。。。。也肥肠常用。。。算法思想是divide and conquer
代码(直接copy了课件上的例程):
#define Cutoff 3int Median3(int A[],int Left,int Right);void Qsort(int A[],int Left,int Right);void Quicksort(int A[],int N){Qsort( A, 0, N - 1 ); /*A:the array*//*0:Left index*//*N-1:Right index*/}int Median3(int A[],int Left,int Right){ int Center=(Left+Right)/2; if(A[Left]>A[Center]) swap(A[Left],A[Center]); if(A[Left]>A[Right]) swap(A[Left],A[Right]); if(A[Center]>A[Right]) swap(A[Center],A[Right]); /* Invariant: A[ Left ] <= A[ Center ] <= A[ Right ] */ swap(A[Center],A[Right-1]); /* Hide pivot */ /* only need to sort A[ Left + 1 ] … A[ Right – 2 ] */ return A[Right-1];/* Return pivot */ }void Qsort(int A[],int Left,int Right){ int i,j; int Pivot; if(Left+Cutoff<=Right){/* if the sequence is not too short */ Pivot=Median3(A,Left,Right);/* select pivot */ i=Left; j=Right-1;/* why not set Left+1 and Right-2? */ for(;;){ while(A[++i]<Pivot){}/* scan from left */while(A[--j]>Pivot){}/* scan from right */if(i<j)swap(A[i],A[j]);/* adjust partition */elsebreak;/* partition done */} swap(A[i],A[Right-1]);/* restore pivot */ Qsort(A,Left,i-1);/* recursively sort left part */ Qsort(A,i+1,Right);/* recursively sort right part */ } /* end if - the sequence is long */ else /* do an insertion sort on the short subarray */ insertion_sort(A+Left,Right-Left+1);}
8、基数排序(???)
暂时不写
9、桶排序(Bucket Sort)
暂时不写
附:
性能比较:
先略。。
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- 常用的排序算法
- SpringMVC学习(一)-HelloWorld
- 常用电平标准及解析
- cookie和session的超详解(配图)
- 技术人员如何提升自己的价值
- 模拟地与数字地详解
- 常用的排序算法
- github -ubuntu 首次git clone 代码错误Please make sure you have the correct access rights and the repositor
- 面向对象-选课系统
- 成年人的思想还能进步么?
- Servlet的生命周期
- 求先序排列(NOIP2001&NOIP水题测试(2017082301))
- CSDN博客的积分计算方法和博客排名规律
- Pg-数据同步详解配置
- 合唱队形