上次分析了Java对primitive的排序,使用的是优化的快速排序算法。这次介绍Java对Object对象(Comparable)的排序。
Java使用了归并对Object排序,关于归并排序的细节,请查看http://zh.wikipedia.org/zh/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F
归并排序也有一个运行状态图,不过看着确实挺费解的 =。=!
在了解了归并排序的原理之后,我们展示Java的源码,来慢慢分析。
首先它克隆了一份拷贝,然后把拷贝和源数组都传入mergeSort方法。就是第一第二个参数。
另外3个参数分别是:low,high,off。low和high没啥多说的,就是上下界,不过off挺有意思的。这个变量没有,也不影响
排序的逻辑。为什么会有呢?先卖个关子。
这段看着非常熟悉,就是当长度小于INSERTIONSORT_THRESHOLD的时候,用插入排序。这个值,还是7.
大家如果还记得在快速排序中,直接hard coding了一个7。可见 sun 的程序员,也是会有失误的!大家都是人,中国程序员
只要不浮躁,还是能够很牛逼的。。。 有点扯远了。
其中的 swap() 就不再累述了。
接下来一段,是当长度大于7时,把数组分两端,分别递归排序。
细心的人已经发现了奥妙的地方,在外部调用mergeSort时,传入的参数顺序是 src, dest。可是它递归时,为什么传入dest,src呢?
位置互换到底代表什么呢?
试想,按照归并的本意,在比较开始之前,需要开辟一个新数组,长度是待比较的两数组和。不过在这Java的源码中,并没有发现这步。那是因为它有效的利用了空间,并没有开辟过多的空间。
大家还记得一开始克隆了数组吗?有了两个数组,他们在排序时,就能够互相利用彼此的空间。就不需要多开辟其他空间了。
下面我一步一步为大家讲解。
首先我们把原先的数组称作 O,克隆的数组称作C。
1. 外部调用mergeSort。按照 C,O的顺序传入。以O为Dest。如果小于7,将O插入排序便结束排序。如果大于7,进入2。待递归返回,将已排序的两部分C的结果,归并入O,结束排序。
2. 以1/2 C为Dest排序。如果C的某一半小于7,将C插入排序然后结束递归。如果大于7,进入3。待递归返回,将已排序的两部分O的结果,归并入C。将有序C返回。
3. 以1/4 O为Dest,继续mergeSort。。。。
。。。迭代ing。。。
n. 全部排序完毕之后,按照之前迭代相反的顺序,将结果归并。最终获得排序的数组O。
究竟怎么归并的呢?正好看下面一段代码。
在整个排序过程中,O和C的角色一直在src和dest中切换。这么做的目的只有一个,节约空间!
下面讲讲用Comparator。其实这个东西一点也不神奇。因为有些用户自定义的类,可能没有继承Comparable,所以sort时无法比较。
那么就可以写一个Comparator,来实现compareTo方法。目的就是告诉sort方法,两个对象,谁大谁小=。= 来帮助sort完成比较,并最终完成排序。
使用了Comparator的sort算法和上面的是基本一致的。就不累述了。
最后,off 这个变量我将揭开谜底!
因为有时,用户会对数组进行部分排序。那么就没必要克隆整个数组(Java对内存的开辟,真是太抠了。。。)。只要复制部分数组。
此时index在O和C就无法对应起来了。怎么办呢? off 变量就记录了这个偏移量,并且在迭代中起到控制的作用。
至此,Arrays sort的排序已经全部学习完毕,留此作为记录,待大家讨论,指点,也供比我更小白的小小白们学习。