原贴:http://www.it165.net/pro/html/201403/10524.html
-
本人介绍的排序算法主要有:插入排序,选择排序,冒泡排序,快速排序,堆排序,归并排序,希尔排序,二叉树排序,桶排序,基数排序(后两者为非比较排序,前面的为比较排序)。
排序的稳定性和复杂度:
不稳定:
选择排序(selection sort)— O(n2)
快速排序(quicksort)— O(nlogn) 平均时间, O(n2) 最坏情况; 对于大的、乱序串列一般认为是最快的已知排序
堆排序 (heapsort)— O(nlogn)
希尔排序 (shell sort)— O(nlogn)
基数排序(radix sort)— O(n·k); 需要 O(n) 额外存储空间 (K为特征个数)
稳定:
插入排序(insertion sort)— O(n2)
冒泡排序(bubble sort) — O(n2)
归并排序 (merge sort)— O(nlogn); 需要 O(n) 额外存储空间
二叉树排序(Binary tree sort) — O(nlogn); 需要 O(n) 额外存储空间
桶排序 (bucket sort)— O(n); 需要 O(k) 额外存储空间
1、插入排序
对于一个序列{a[0]……a[n]},当记录值是第i个元素时,前面i-1个元素已经排好序了,那么这个记录值从第i-1个元素一直往前比较,找到属于它的位置后插进去。
01.1#include <iostream>02.2using namespace std;03.304.4intmain()05.5{06.6inta[]={1,99,2,88,3,77,4,66};07.7intn=sizeof(a)/4;08.8for(inti=0; i<n; i++)09.9{10.10inttp=a[i], j;11.11for(j=i-1; j>=0&&a[j]>tp; j--) a[j+1]=a[j];12.12a[j+1]=tp;13.13}14.14cout << a[0] ;15.15for(inti=1; i<n; i++) cout <<' '<< a[i];16.16cout <<endl;17.17return0;18.18}
2、选择排序
对于一个序列{a[0]……a[n]},前面i-1个元素都是已经排好序的,那么从第i到第n个元素,找到最小值的那个元素,如果下标不是i,则让第i个元素和那个最小的元素位置互换。
01.1#include <iostream>02.2using namespace std;03.304.4intmain()05.5{06.6inta[]={1,99,2,88,3,77,4,66};07.7intn=sizeof(a)/4;08.8for(inti=0; i<n; i++)09.9{10.10intpos=-1, minn=a[i];11.11for(intj=i+1; j<n; j++)12.12{13.13if(a[j]<minn) minn=a[j], pos=j;14.14}15.15if(pos!=-1) swap(a[i],a[pos]);16.16}17.17cout << a[0] ;18.18for(inti=1; i<n; i++) cout <<' '<< a[i];19.19cout <<endl;20.20return0;21.21}
3、冒泡排序
冒泡排序顾名思义就是从最后往前两个元素开始进行两两比较,如果a[i]小于a[i-1],那么让他们互换位置,每比较一轮必有一个最小的元素冒泡到这些所比较元素的前面。
01.1#include <iostream>02.2using namespace std;03.304.4intmain()05.5{06.6inta[]={1,99,2,88,3,77,4,66};07.7intn=sizeof(a)/4;08.8for(inti=0; i<n; i++)09.9{10.10for(intj=n-1; j>i; j--)11.11if(a[j]<a[j-1]) swap(a[j],a[j-1]);12.12}13.13cout << a[0] ;14.14for(inti=1; i<n; i++) cout <<' '<< a[i];15.15cout <<endl;16.16return0;17.17}
4、快速排序
基本思想就是取一个数作为中间数(一般是取第一个数作为中间数),小于它的都放到左边,大于它的都放到右边,再对每一边利用同样的思想进行处理。
01.1#include <iostream>02.2using namespace std;03.304.4voidQuickSort(int*a,intl,intr)05.5{06.6if(a==NULL||l>=r)return;07.708.8inti=l, j=r, tmp=a[l];09.9while(i<j)10.10{11.11while(j>i&&a[j]>=tmp) j--;12.12a[i]=a[j];13.13while(i<j&&a[i]<=tmp) i++;14.14a[j]=a[i];15.15}16.16a[i]=tmp;17.17QuickSort(a,l,i-1);18.18QuickSort(a,i+1,r);19.19}20.2021.21intmain()22.22{23.23inta[]= {1,99,2,88,3,77,4,66};24.24intn=sizeof(a)/4;25.25QuickSort(a,0,n-1);26.26cout << a[0] ;27.27for(inti=1; i<n; i++) cout <<' '<< a[i];28.28cout <<endl;29.29return0;30.30}
5、堆排序
堆排序其实要利用到二叉堆,二叉堆其实完全可以理解为一颗有限制的完全二叉树。
二叉堆的定义:二叉堆可以分为最大堆和最小堆。最大堆为对于所有节点它的左右节点权值一定比它小,最小堆为对于所有节点它的左右节点权值一定比它大。
二叉堆的插入:将一个序列下表从0开始一个一个往堆里插入,因为满足完全二叉树性质,所以这么做是可行的。对于插入的第i个数,那么从下往上,它的父亲节点为(i-1)/2个数,再根据二叉堆的性质进行调整。
二叉堆的删除:每次进行一次堆调整之后,根节点必是最大的(最大堆),每次把根节点a[0]取出和数组第n个数互换,然后再用数组第1个到第n-1个数再次建堆,如此反复取出再建堆,那么得到的新序列必是一个有序序列。
01.1#include <iostream>02.2using namespace std;03.304.4voidBuildHeap(int*a,inti,intn)//建二叉堆05.5{06.6intj=(i-1)/2;//j为i节点的父亲节点07.7while(i>0)08.8{09.9if(a[j]>=a[i])break;10.10swap(a[i],a[j]);11.11i=j;12.12j=(i-1)/2;13.13}14.14}15.1516.16voidMaxHeapSort(int*a,inti,intn)//二叉堆排序17.17{18.18intj=2*i+1, tmp=a[i];19.19while(j<n)20.20{21.21if(a[j+1]>a[j]&&j+1<n) j++;//选出i节点左右孩子节点的最大值22.22if(tmp>=a[j])break;23.23a[i]=a[j];24.24i=j;25.25j=2*i+1;26.26}27.27a[i]=tmp;28.28}29.2930.3031.31intmain()32.32{33.33inta[]= {1,99,2,88,3,77,4,66};34.34intn=sizeof(a)/4;35.35for(inti=0; i<=n-1; i++)36.36BuildHeap(a,i,n);37.37for(inti=n-1; i>=1; i--)38.38{39.39swap(a[i],a[0]);40.40MaxHeapSort(a,0,i);41.41}42.42cout << a[0] ;43.43for(inti=1; i<n; i++) cout <<' '<< a[i];44.44cout <<endl;45.45return0;46.46}
6、归并排序
归并的意思就是两个或两个以上的有序表组合成一个新的有序表。整个归并排序需要进行【lgn取上限】次,总的时间复杂度为O(nlgn)。与快速排序相比,归并排序的最大特点是:它是一种稳定的排序方法。

01.1#include <iostream>02.2using namespace std;03.304.4voidMerge(int*a,ints,intm,intt)05.5{06.6int*b=newint[10];07.7inti=s, j=m+1, num=0;08.8while(i<=m&&j<=t)09.9{10.10if(a[i]<=a[j]) b[num++]=a[i], i++;11.11elseb[num++]=a[j],j++;12.12}13.13while(i<=m) b[num++]=a[i], i++;14.14while(j<=t) b[num++]=a[j], j++;15.15for(inti=s; i<=t; i++) a[i]=b[i-s];16.16delete[] b;17.17}18.1819.19voidMergeSort(int*a,ints,intt)20.20{21.21if(a==NULL)return;22.22if(s<t)23.23{24.24intmid=(s+t)/2;25.25MergeSort(a,s,mid);26.26MergeSort(a,mid+1,t);27.27Merge(a,s,mid,t);28.28}29.29}30.3031.3132.32intmain()33.33{34.34inta[]= {1,99,2,88,3,77,4,66};35.35intn=sizeof(a)/4;36.36MergeSort(a,0,n-1);37.37cout << a[0] ;38.38for(inti=1; i<n; i++) cout <<' '<< a[i];39.39cout <<endl;40.40return0;41.41}
7、希尔排序
很多人都说希尔排序是插入排序的一种改进,我看了半天也没看明白这句话。
希尔排序就是利用无空位的跳跃值gap进行跳跃排序,如果n为8,为gap的取值则为8 4 2 1, gap=gap/2,gap初始值为n/2。
对于每个gap值,都要从后往前扫一遍(以gap值大小跳跃比较),即i,i-gap,i-2*gap……。注意:只限在相邻两个扫到的数比较。
时间复杂度:O(nlog(n*n))。
01.1#include <iostream>02.2using namespace std;03.304.4voidShellSort(int*a,intn)05.5{06.6for(intgap=n/2; gap>0; gap/=2)07.7for(inti=gap; i<n; i++)08.8for(intj=i-gap; j>=0; j-=gap)09.9if(a[j]>a[j+gap]) swap(a[j],a[j+gap]);10.10}11.1112.12intmain()13.13{14.14inta[]= {1,99,2,88,3,77,4,66,123,321,58,324,127,428};15.15intn=sizeof(a)/4;16.16ShellSort(a,n);17.17cout << a[0];18.18for(inti=1; i<n; i++) cout <<' '<< a[i];19.19cout << endl;20.20return0;21.21}
8、二叉树排序
二叉树的性质:对于每个节点,它的左孩子的键值一定比它小,右孩子的键值一定比它大。
二叉树排序简单点说就是先随便设置一个根节点,然后将其他数一个一个插入到树中,权值小于此节点则往左走,大于往右走,一直找到合适的位置建立自己新的节点位置。
01.1#include <iostream>02.2#include <cstring>03.3#include <sstream>04.4#include <algorithm>05.5using namespace std;06.607.7struct Node08.8{09.9intkey;10.10Node *l, *r;11.11Node(){ l=NULL; r=NULL;}12.12};13.1314.14Node* Insert(Node *rt,intkey)15.15{16.16if(rt==NULL)17.17{18.18Node *rt=newNode();19.19rt->key=key;20.20returnrt;21.21}22.22if(key<rt->key) rt->l=Insert(rt->l,key);23.23elsert->r=Insert(rt->r,key);24.24returnrt;25.25}26.2627.27voidPrintf(Node *rt)28.28{29.29if(rt->l!=NULL) Printf(rt->l);30.30cout << rt->key <<' ';31.31if(rt->r!=NULL) Printf(rt->r);32.32}33.3334.34intmain()35.35{36.36inta[]= {1,99,2,88,3,77,4,66,123,321,58,324,127,428};37.37intn=sizeof(a)/4;38.38Node *root=newNode();39.39root->key=a[0];40.40for(inti=1; i<n; i++)41.41{42.42Insert(root,a[i]);43.43}44.44Printf(root);45.45cout << endl;46.46return0;47.47}
9、基数排序
基数是一种不稳定的排序,它的时间复杂度为O(k*n),k表示最大数的位数,所以当一个序列中有一个很大很大的数时,它排序所花费的时间是非常高昂的。
基数排序的原理是一位一位来排序:先按个位大小排序,再按十位大小排序,接着百位……,一直排到最大位数停止。
比如这样一个数列排序: 342 ,58, 576, 356
第一次排序(个位):
3 4 2
5 7 6
3 5 6
0 5 8
第二次排序(十位):
3 4 2
3 5 6
0 5 8
5 7 6
第三次排序(百位):
0 5 8
3 4 2
3 5 6
5 7 6
结果: 58 342 356 576。
01.1#include <iostream>02.2#include <cstring>03.3#include <sstream>04.4#include <algorithm>05.5using namespace std;06.607.7intmaxdigit(int*a,intn)//返回数组中最大数的位数08.8{09.9intmaxx=0;10.10for(inti=0; i<n; i++)11.11{12.12stringstream sa;13.13sa<<a[i];14.14string s=sa.str();15.15maxx=max(maxx,int(s.size()));16.16}17.17returnmaxx;18.18}19.1920.20voidBaseSort(int*a,intn)21.21{22.22int*count=newint[10];23.23int*tmp=newint[n];24.24intm=maxdigit(a,n);25.25intbase=1;26.26for(inti=1; i<=m; i++)27.27{28.28for(intj=0; j<10; j++) count[j]=0;29.29for(intj=0; j<n; j++)30.30{31.31intk=a[j]/base%10;32.32count[k]++;33.33}34.34for(intj=1; j<10; j++)35.35count[j]+=count[j-1];36.36for(intj=n-1; j>=0; j--)37.37{38.38intk=a[j]/base%10;39.39count[k]--;40.40tmp[ count[k] ]=a[j];41.41}42.42for(intj=0; j<n; j++) a[j]=tmp[j];43.43base*=10;44.44}45.45delete[] count;46.46delete[] tmp;47.47}48.4849.49intmain()50.50{51.51inta[]= {1,99,2,88,3,77,4,66,123,321,58,324,127,428};52.52intn=sizeof(a)/4;53.53BaseSort(a,n);54.54cout << a[0] ;55.55for(inti=1; i<n; i++) cout <<' '<< a[i];56.56cout <<endl;57.57return0;58.58} -
本文介绍了九种常用的排序算法,包括插入排序、选择排序、冒泡排序等,并详细阐述了各自的稳定性、时间复杂度及实现原理。

31万+

被折叠的 条评论
为什么被折叠?



