排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。
常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。
其中关于时间复杂度、稳定性等对比如下图:
当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。
快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
(1)直接插入排序:一般插入排序,比较是从有序序列的最后一个元素开始,如果比它大则直接插入在其后面,否则一直往前比。如果找到一个和插入元素相等的,那么就插入到这个相等元素的后面。插入排序是稳定的。
(2)希尔排序:希尔排序是按照不同步长对元素进行插入排序,一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,稳定性就会被破坏,所以希尔排序不稳定。
(3)简单选择排序:在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。光说可能有点模糊,来看个小实例:858410,第一遍扫描,第1个元素8会和4交换,那么原序列中2个8的相对前后顺序和原序列不一致了,所以选择排序不稳定。
(4)堆排序:堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n/2-1, n/2-2, …这些父节点选择元素时,有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没有交换,所以堆排序并不稳定。
(5)冒泡排序:由前面的内容可知,冒泡排序是相邻的两个元素比较,交换也发生在这两个元素之间,如果两个元素相等,不用交换。所以冒泡排序稳定。
(6)快速排序:在中枢元素和序列中一个元素交换的时候,很有可能把前面的元素的稳定性打乱。还是看一个小实例:6 4 4 5 4 7 8 9,第一趟排序,中枢元素6和第三个4交换就会把元素4的原序列破坏,所以快速排序不稳定。
(7)归并排序:在分解的子列中,有1个或2个元素时,1个元素不会交换,2个元素如果大小相等也不会交换。在序列合并的过程中,如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,所以,归并排序也是稳定的。
(8)基数排序:是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序,最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。
#include<stdio.h>
//直接插入
void insertSort(int a[],int n)
{
int i,j,temp,s=0;
for(i=1;i<n;i++)
{
if(a[i]<a[i-1])
{
temp=a[i];
for(j=i;j>0&&a[j-1]>temp;j--)
{
a[j]=a[j-1];
}
a[j]=temp;
}
}
}
//希尔排序
void shellSort(int a[],int n,int k){
int i,j,temp;
for(i=k;i*k<n;i++){
if(a[i]<a[i-k]){
temp=a[i];
for(j=i;j>0&&a[j-k]>temp;j-=k){
a[j]=a[j-k];
}
a[j]=temp;
}
}
}
//选择排序
selectSort(int a[],int n){
int i,j,k,t;
for(i=0;i<n;i++){
k=i;
for(j=i+1;j<n;j++){
if(a[j]<a[k])
k=j;
}
if(k!=i)
{
t=a[i];
a[i]=a[k];
a[k]=t;
}
}
}
//冒泡排序
bubbleSort(int a[],int n)
{
int i,j,t;
for(i=0;i<n;i++){
for(j=0;j<n-i;j++){
if(a[j]>a[j+1])
{
t=a[j+1];
a[j+1]=a[j];
a[j]=t;
}
}
}
}
//归并
void merge(int *a,int m,int *b,int n)
{
int *p=a,*q=b;
int i,j=0,k=0;
int c[9];
for(i=0;i<m+n;i++)
{
if(j<m&&k<n)
{
if(b[k]>a[j]){
c[i]=a[j];
j++;
}
else{
c[i]=b[k];
k++;
}
}else{
if(k==n){
c[i]=a[j];
j++;
}
else{
c[i]=b[k];
k++;
}
}
}
for(i=0;i<n+m;i++)
{
a[i]=c[i];
}
}
//归并排序
mergeSort(int a[],int n)
{
int *p,*q,plength,qlength;
if(n>1)
{
p=a;
plength=n/2;
q=a+n/2;
qlength=n-plength;
mergeSort(p,plength);
mergeSort(q,qlength);
merge(p,plength,q,qlength);
}
}
//快速排序
void quicklySort(int a[],int l,int r)
{
int temp,t,i,j;
if(l>r)
return;
temp=a[l];
i=l;
j=r;
while(i!=j)
{
//顺序很重要,要先从右边开始找
while(a[j]>=temp && i<j)
j--;
//再找右边的
while(a[i]<=temp && i<j)
i++;
//交换两个数在数组中的位置
if(i<j)
{
t=a[i];
a[i]=a[j];
a[j]=t;
}
}
a[l]=a[i];
a[i]=temp;
quicklySort(a,l,i-1);
quicklySort(a,i+1,r);
}
//基数排序.求出第k+1位的数值
int pos(int n,int k)
{
int radix=1,i,num;
for(i=0;i<k;i++)
radix*=10;
num=(n/radix)%10;
return num;
}
//基数排序
void radixSort(int a[],int n)
{
int *radix[10];
int i,j,k,m,num,index;
for(i=0;i<N;i++)
{
radix[i]=(int*)malloc(sizeof(int)*(N+1));
radix[i][0]=0;
}
for(m=0;m<3;m++)
{
for(i=0;i<n;i++)
{
num=pos(a[i],m);
radix[num][0]++;
index=radix[num][0];
radix[num][index]=a[i];
}
k=0;
for(i=0;i<N;i++)
{
for(j=1;j<radix[i][0]+1;j++)
{
a[k]=radix[i][j];
k++;
}
radix[i][0]=0;
}
}
}
void main(){
int a[7]={4,7,32,76,1,9,2};
int n=7,i,j;
//insertSort(a,n);
//mergeSort(a,n);
//selectSort(a,n);
//shellSort(a,n);
//quicklySort(a,0,n-1);
for(j=5;j>0;j=j/2)
{
bubbleSort(a,n);
for(i=0;i<7;i++)
{
printf("%d ",a[i]);
}
printf("\n");
}
}