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; } }