总结一下常见的几种排序

来源:互联网 发布:算法的发展成果 编辑:程序博客网 时间:2024/06/15 00:20

1、插入排序

public void insertsort(int[] a){    for(int i=1;i<a.length;i++)    {        for(int j=i;j>0;j--)        {            if(a[j]<a[j-1])                swap(a,j,j-1);        }    }}
时间复杂度O(n^2),没有多余的空间开销,虽然复杂度为平方级别,但在数组为近似有序的情况下效率非常高,有时在归并和快排的的子序列中可以使用插入排序,一般能提升20%-30%的效率。

2、希尔排序

希尔排序可以理解为插入排序的改进,大概思想是不断循环把数组根据间隔进行部分排序,直至间隔为1,这样做充分利用了插入排序对近似有序数组排序的高效特点,效率比插入排序提高70%-80%。

public void hillsort(int[] a){    int length=a.length;    int h=1;    while(h<length/3)    {        h=h*3+1;    }    while(h>=1)    {        for(int i=h;i<length;i++)        {            for(int j=i;j>=h;j-=h)            {                if(a[j]<a[j-h])                {                    swap(a,j,j-h);                }            }        }        h/=3;    }}
这里的间隔取值是按《Algorithms》书中取得,一般来说这样的间隔取值能达到最优速度。

3、快速排序

快排的理论最差时间复杂度为O(n^2),但实际运行速度还是很快的,平均效率为O(nlgn),通过随机化key值基本可以规避掉最坏情况。笔者认为快排是一种很优秀的排序,内循环极为精简,比较次数很少,通过随机化快排切分数组的效果也很好。

public void quicksort(int[] a,int begin,int end){    if(begin>=end) return;    int temp=a[begin];    int key=begin;    for(int j=begin+1;j<=end;j++)    {        if(a[j]<temp) {            swap(a, key + 1, j);            key++;        }    }    swap(a,begin,key);    quicksort(a,begin,key-1);    quicksort(a,key+1,end);}
4、归并排序

归并排序的理论时间复杂度应该是排序中最优秀的了,但实际运行中其实速度却不是最快的,因为归并排序的代码操作非常多,因此性能并不像理论上表现的那么优秀,还有一个问题是归并排序需要额外的空间复杂度,与数组长度成正比,因此也要根据实际情况选择。

public void mergesort(int[] a,int begin,int end){    if(begin>=end) return;    int mid=(begin+end)/2;    mergesort(a,begin,mid);    mergesort(a,mid+1,end);    merge(a,begin,mid,end);}public void merge(int[] a,int begin,int mid,int end){    int[] temp=new int[end-begin+1];    int i=begin,j=mid+1,k=0;    while(i<=mid&&j<=end)    {        if(a[i]<a[j])        {            temp[k++]=a[i++];        }        else{            temp[k++]=a[j++];        }    }    while(i<=mid){        temp[k++]=a[i++];    }    while(j<=end){        temp[k++]=a[j++];    }    for(int m=0;m<temp.length;m++){        a[m+begin]=temp[m];    }}
5、堆排序

二叉堆的维护分为插入和删除两种情况,即上浮和下沉两种操作,如果只是用堆排序的话只用到下沉就可以了,堆排序是目前所知能够同时最优的利用空间和时间的方法,它的缺点也很明显,无法利用缓存,因为很少与相邻的其他元素进行比较,因此缓存未命中的概率远高于快速排序、归并排序等排序。

public void heapsort(int[] a){    int length=a.length;    for(int i=(length-1)/2;i>=0;i--)    {        sink(a,i,length-1);    }    int n=length-1;    while(n>0){        swap(a,n--,0);        sink(a,0,n);    }}public void sink(int[] a,int k,int n){    while(2*k+1<=n)    {        int j=2*k+1;        if(j<n&&a[j]<a[j+1])            j++;        if(a[k]>a[j]) break;        swap(a,j,k);        k=j;    }}

0 0