发表时间:2008.11.17
编 者:长空飞鹰(http://hi.youkuaiyun.com/oanqoanq)
仔细分析java的Arrays.sort(version 1.71, 04/21/06)后发现,java对
primitive(int,float等原型数据)数组采用快速排序,对Object对象数组采用归并排序。
对这一区别,sun在<<The Java Tutorial>>中做出的解释是:
The sort operation uses a slightly optimized merge sort algorithm that is fast and stable:
* Fast: It is guaranteed to run in n log(n) time and runs substantially faster on nearly sorted lists. Empirical tests showed it to be as fast as a highly optimized quicksort. A quicksort is generally considered to be faster than a merge sort but isn't stable and doesn't guarantee n log(n) performance.
* Stable: It doesn't reorder equal elements. This is important if you sort the same list repeatedly on different attributes. If a user of a mail program sorts the inbox by mailing date and then sorts it by sender, the user naturally expects that the now-contiguous list of messages from a given sender will (still) be sorted by mailing date. This is guaranteed only if the second sort was stable.
也就是说,优化的归并排序既快速(nlog(n))又稳定。
对于对象的排序,稳定性很重要。比如成绩单,一开始可能是按人员的学号顺序排好了的,现在让我们用成绩排,那么你应该保证,本来张三在李四前面,即使他们成绩相同,张三不能跑到李四的后面去。
而快速排序是不稳定的,而且最坏情况下的时间复杂度是O(n^2)。
另外,对象数组中保存的只是对象的引用,这样多次移位并不会造成额外的开销,但是,对象数组对比较次数一般比较敏感,有可能对象的比较比单纯数的比较开销大很多。归并排序在这方面比快速排序做得更好,这也是选择它作为对象排序的一个重要原因之一。
排序优化:实现中快排和归并都采用递归方式,而在递归的底层,也就是待排序的数组长度小于7时, 直接使用冒泡排序,而不再递归下去。
分析:长度为6的数组冒泡排序总比较次数最多也就1+2+3+4+5+6=21次,最好情况下只有6次比较。而快排或归并涉及到递归调用等的开销,其时间效率在n较小时劣势就凸显了,因此这里采用了冒泡排序,这也是对快速排序极重要的优化。
<meta content="OneNote.File" name="ProgId"><meta content="Microsoft OneNote 12" name="Generator">
附源码:
- /*快速排序*/
- privatestaticvoidsort1(intx[],intoff,intlen){
- //Insertionsortonsmallestarrays
- if(len<7){
- for(inti=off;i<len+off;i++)
- for(intj=i;j>off&&x[j-1]>x[j];j--)
- swap(x,j,j-1);
- return;
- }
- //Chooseapartitionelement,v
- intm=off+(len>>1);//Smallarrays,middleelement
- if(len>7){
- intl=off;
- intn=off+len-1;
- if(len>40){//Bigarrays,pseudomedianof9
- ints=len/8;
- l=med3(x,l,l+s,l+2*s);//取后三个参数中的中间值
- m=med3(x,m-s,m,m+s);
- n=med3(x,n-2*s,n-s,n);
- }
- m=med3(x,l,m,n);//Mid-size,medof3
- }
- intv=x[m];
- //EstablishInvariant:v*(<v)*(>v)*v*
- inta=off,b=a,c=off+len-1,d=c;
- while(true){
- while(b<=c&&x[b]<=v){
- if(x[b]==v)
- swap(x,a++,b);
- b++;
- }
- while(c>=b&&x[c]>=v){
- if(x[c]==v)
- swap(x,c,d--);
- c--;
- }
- if(b>c)
- break;
- swap(x,b++,c--);
- }
- //Swappartitionelementsbacktomiddle
- ints,n=off+len;
- s=Math.min(a-off,b-a);vecswap(x,off,b-s,s);
- s=Math.min(d-c,n-d-1);vecswap(x,b,n-s,s);
- //Recursivelysortnon-partition-elements
- if((s=b-a)>1)
- sort1(x,off,s);
- if((s=d-c)>1)
- sort1(x,n-s,s);
- }
- /*归并排序*/
- privatestaticvoidmergeSort(Object[]src,
- Object[]dest,
- intlow,
- inthigh,
- intoff){
- intlength=high-low;
- //Insertionsortonsmallestarrays
- if(length<INSERTIONSORT_THRESHOLD){
- for(inti=low;i<high;i++)
- for(intj=i;j>low&&
- ((Comparable)dest[j-1]).compareTo(dest[j])>0;j--)
- swap(dest,j,j-1);
- return;
- }
- //Recursivelysorthalvesofdestintosrc
- intdestLow=low;
- intdestHigh=high;
- low+=off;
- high+=off;
- intmid=(low+high)>>>1;
- mergeSort(dest,src,low,mid,-off);
- mergeSort(dest,src,mid,high,-off);
- //Iflistisalreadysorted,justcopyfromsrctodest.Thisisan
- //optimizationthatresultsinfastersortsfornearlyorderedlists.
- if(((Comparable)src[mid-1]).compareTo(src[mid])<=0){
- System.arraycopy(src,low,dest,destLow,length);
- return;
- }
- //Mergesortedhalves(nowinsrc)intodest
- for(inti=destLow,p=low,q=mid;i<destHigh;i++){
- if(q>=high||p<mid&&((Comparable)src[p]).compareTo(src[q])<=0)
- dest[i]=src[p++];
- else
- dest[i]=src[q++];
- }
- }