java.util.ComparableTimSort中的sort()方法简单分析
TimSort算法是一種起源于歸并排序和插入排序的混合排序算法,設(shè)計(jì)初衷是為了在真實(shí)世界中的各種數(shù)據(jù)中能夠有較好的性能。
該算法最初是由Tim Peters于2002年在Python語(yǔ)言中提出的。
TimSort 是一個(gè)歸并排序做了大量?jī)?yōu)化的版本號(hào)。
對(duì)歸并排序排在已經(jīng)反向排好序的輸入時(shí)表現(xiàn)O(n2)的特點(diǎn)做了特別優(yōu)化。對(duì)已經(jīng)正向排好序的輸入降低回溯。對(duì)兩種情況混合(一會(huì)升序。一會(huì)降序)的輸入處理比較好。
在jdk1.7之后。Arrays類中的sort方法有一個(gè)分支推斷,當(dāng)LegacyMergeSort.userRequested為true的情況下,採(cǎi)用legacyMergeSort,否則採(cǎi)用ComparableTimSort。而且在legacyMergeSort的凝視上標(biāo)明了該方法會(huì)在以后的jdk版本號(hào)中廢棄,因此以后Arrays類中的sort方法將採(cǎi)用ComparableTimSort類中的sort方法。
<span style="font-family:Microsoft YaHei;">public static void sort(Object[] a, int fromIndex, int toIndex) {if (LegacyMergeSort.userRequested)legacyMergeSort(a, fromIndex, toIndex);elseComparableTimSort.sort(a, fromIndex, toIndex); } </span>以下是ComparableTimSort的sort方法<span style="font-family:Microsoft YaHei;">static void sort(Object[] a) {sort(a, 0, a.length); }static void sort(Object[] a, int lo, int hi) {rangeCheck(a.length, lo, hi);int nRemaining = hi - lo;if (nRemaining < 2)return; // Arrays of size 0 and 1 are always sorted// If array is small, do a "mini-TimSort" with no mergesif (nRemaining < MIN_MERGE) {int initRunLen = countRunAndMakeAscending(a, lo, hi);binarySort(a, lo, hi, lo + initRunLen);return;}/*** March over the array once, left to right, finding natural runs,* extending short natural runs to minRun elements, and merging runs* to maintain stack invariant.*/ComparableTimSort ts = new ComparableTimSort(a);int minRun = minRunLength(nRemaining);do {// Identify next runint runLen = countRunAndMakeAscending(a, lo, hi);// If run is short, extend to min(minRun, nRemaining)if (runLen < minRun) {int force = nRemaining <= minRun ? nRemaining : minRun;binarySort(a, lo, lo + force, lo + runLen);runLen = force;}// Push run onto pending-run stack, and maybe mergets.pushRun(lo, runLen);ts.mergeCollapse();// Advance to find next runlo += runLen;nRemaining -= runLen;} while (nRemaining != 0);// Merge all remaining runs to complete sortassert lo == hi;ts.mergeForceCollapse();assert ts.stackSize == 1; }</span>(1)傳入的待排序數(shù)組若小于閾值MIN_MERGE(Java實(shí)現(xiàn)中為32。Python實(shí)現(xiàn)中為64)。則調(diào)用 binarySort,這是一個(gè)不包括合并操作的 mini-TimSort。a) 從數(shù)組開(kāi)始處找到一組連接升序或嚴(yán)格降序(找到后翻轉(zhuǎn))的數(shù)
b) Binary Sort:使用二分查找的方法將興許的數(shù)插入之前的已排序數(shù)組。binarySort 對(duì)數(shù)組 a[lo:hi] 進(jìn)行排序,而且a[lo:start] 是已經(jīng)排好序的。算法的思路是對(duì)a[start:hi] 中的元素。每次使用binarySearch 為它在 a[lo:start] 中找到對(duì)應(yīng)位置,并插入。
(2)開(kāi)始真正的TimSort過(guò)程:
????? (2.1) 選取minRun大小,之后待排序數(shù)組將被分成以minRun大小為區(qū)塊的一塊塊子數(shù)組
a) 假設(shè)數(shù)組大小為2的N次冪,則返回16(MIN_MERGE / 2)
b) 其它情況下,逐位向右位移(即除以2),直到找到介于16和32間的一個(gè)數(shù)
- minRun
MIN_MERGE 默覺(jué)得32,假設(shè)n小于此值,那么返回n 本身。否則會(huì)將 n 不斷地右移。直到少于 MIN_MERGE,同一時(shí)候記錄一個(gè) r 值,r 代表最后一次移位n時(shí)。n最低位是0還是1。 最后返回 n + r,這也意味著僅僅保留最高的 5 位。再加上第六位。
(2.2)do-while
(2.2.1)找到初始的一組升序數(shù)列,countRunAndMakeAscending 會(huì)找到一個(gè)run 。這個(gè)run 必須是已經(jīng)排序的。而且函數(shù)會(huì)保證它為升序,也就是說(shuō),假設(shè)找到的是一個(gè)降序的。會(huì)對(duì)其進(jìn)行翻轉(zhuǎn)。
(2.2.2)若這組區(qū)塊大小小于minRun,則將興許的數(shù)補(bǔ)足,利用binarySort 對(duì) run 進(jìn)行擴(kuò)展。而且擴(kuò)展后,run 仍然是有序的。
(2.2.3)當(dāng)前的 run 位于 a[lo:runLen] ,將其入棧ts.pushRun(lo, runLen);//為興許merge各區(qū)塊作準(zhǔn)備:記錄當(dāng)前已排序的各區(qū)塊的大小
(2.2.4)對(duì)當(dāng)前的各區(qū)塊進(jìn)行merge,merge會(huì)滿足下面原則(如果X,Y,Z為相鄰的三個(gè)區(qū)塊):
a) 僅僅對(duì)相鄰的區(qū)塊merge
b) 若當(dāng)前區(qū)塊數(shù)僅為2,If X<=Y。將X和Y merge
b) 若當(dāng)前區(qū)塊數(shù)>=3,If X<=Y+Z。將X和Y merge。直到同一時(shí)候滿足X>Y+Z和Y>Z
因?yàn)橐喜⒌膬蓚€(gè) run 是已經(jīng)排序的,所以合并的時(shí)候,有會(huì)特別的技巧。如果兩個(gè) run 是 run1,run2 ,先用 gallopRight在 run1 里使用 binarySearch 查找run2 首元素 的位置k, 那么 run1 中 k 前面的元素就是合并后最小的那些元素。然后,在run2 中查找run1 尾元素 的位置 len2 ,那么run2 中 len2 后面的那些元素就是合并后最大的那些元素。最后,依據(jù)len1 與len2 大小。調(diào)用mergeLo 或者 mergeHi 將剩余元素合并。
(2.2.5) 反復(fù)2.2.1 ~ 2.2.4,直到將待排序數(shù)組排序完?
(2.2.6) Final Merge:假設(shè)此時(shí)還有區(qū)塊未merge,則合并它們
?(3)演示樣例
*注意*:為了演示方便,我將TimSort中的minRun直接設(shè)置為2,否則我不能用非常小的數(shù)組演示。。
。同一時(shí)候把MIN_MERGE也改成2(默覺(jué)得32),這樣避免直接進(jìn)入binary sort。
初始數(shù)組為[7,5,1,2,6,8,10,12,4,3,9,11,13,15,16,14]
=> 尋找連續(xù)的降序或升序序列 (2.2.1)。同一時(shí)候countRunAndMakeAscending 函數(shù)會(huì)保證它為升序
[1,5,7] [2,6,8,10,12,4,3,9,11,13,15,16,14]
=> 入棧 (2.2.3)
當(dāng)前的棧區(qū)塊為[3]
=> 進(jìn)入merge循環(huán) (2.2.4)
do not merge由于棧大小僅為1
=> 尋找連續(xù)的降序或升序序列 (2.2.1)
[1,5,7] [2,6,8,10,12] [4,3,9,11,13,15,16,14]
=> 入棧 (2.2.3)
當(dāng)前的棧區(qū)塊為[3, 5]
=> 進(jìn)入merge循環(huán) (2.2.4)
merge由于runLen[0]<=runLen[1]
1) gallopRight:尋找run1的第一個(gè)元素應(yīng)當(dāng)插入run0中哪個(gè)位置(”2”應(yīng)當(dāng)插入”1”之后),然后就能夠忽略之前run0的元素(都比run1的第一個(gè)元素小)
2) gallopLeft:尋找run0的最后一個(gè)元素應(yīng)當(dāng)插入run1中哪個(gè)位置(”7”應(yīng)當(dāng)插入”8”之前),然后就能夠忽略之后run1的元素(都比run0的最后一個(gè)元素大)
這樣須要排序的元素就僅剩下[5,7] [2,6],然后進(jìn)行mergeLow
完畢之后的結(jié)果:
[1,2,5,6,7,8,10,12] [4,3,9,11,13,15,16,14]
=> 入棧 (2.2.3)
當(dāng)前的棧區(qū)塊為[8]
退出當(dāng)前merge循環(huán)由于棧中的區(qū)塊僅為1
=> 尋找連續(xù)的降序或升序序列 (2.2.1)
[1,2,5,6,7,8,10,12] [3,4] [9,11,13,15,16,14]
=> 入棧 (2.2.3)
當(dāng)前的棧區(qū)塊大小為[8,2]
=> 進(jìn)入merge循環(huán) (2.2.4)
do not merge由于runLen[0]>runLen[1]
=> 尋找連續(xù)的降序或升序序列 (2.2.1)
[1,2,5,6,7,8,10,12] [3,4] [9,11,13,15,16] [14]
=> 入棧 (2.2.3)
當(dāng)前的棧區(qū)塊為[8,2,5]
=>
do not merege run1與run2由于不滿足runLen[0]<=runLen[1]+runLen[2]
merge run2與run3由于runLen[1]<=runLen[2]
1) gallopRight:發(fā)現(xiàn)run1和run2就已經(jīng)排好序
完畢之后的結(jié)果:
[1,2,5,6,7,8,10,12] [3,4,9,11,13,15,16] [14]
=> 入棧 (2.2.3)
當(dāng)前入棧的區(qū)塊大小為[8,7]
退出merge循環(huán)由于runLen[0]>runLen[1]
=> 尋找連續(xù)的降序或升序序列 (2.2.1)
最后僅僅剩下[14]這個(gè)元素:[1,2,5,6,7,8,10,12] [3,4,9,11,13,15,16] [14]
=> 入棧 (2.2.3)
當(dāng)前入棧的區(qū)塊大小為[8,7,1]
=> 進(jìn)入merge循環(huán) (2.2.4)
merge由于runLen[0]<=runLen[1]+runLen[2]
由于runLen[0]>runLen[2],所以將run1和run2先合并。(否則將run0和run1先合并)
1) gallopRight & 2) gallopLeft
這樣須要排序的元素剩下[13,15] [14],然后進(jìn)行mergeHigh
完畢之后的結(jié)果:
[1,2,5,6,7,8,10,12] [3,4,9,11,13,14,15,16] 當(dāng)前入棧的區(qū)塊為[8,8]
=>
繼續(xù)merge由于runLen[0]<=runLen[1]
1) gallopRight & 2) gallopLeft
須要排序的元素剩下[5,6,7,8,10,12] [3,4,9,11]。然后進(jìn)行mergeHigh
完畢之后的結(jié)果:
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] 當(dāng)前入棧的區(qū)塊大小為[16]
=>
不須要final merge由于當(dāng)前棧大小為1
=>
結(jié)束
參考:
http://www.lifebackup.cn/timsort-java7.html
http://blog.csdn.net/on_1y/article/details/30109975
http://en.wikipedia.org/wiki/Timsort
轉(zhuǎn)載于:https://www.cnblogs.com/lxjshuju/p/7081959.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的java.util.ComparableTimSort中的sort()方法简单分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Math常用方法,String转floa
- 下一篇: 金字塔原理(Pyramid Princi