前情提要
在Worst Case下
O(NlgN)是最佳狀況
所以我們不要求random時多好
不要退化
O(N) -> O(NlgN)
1.利用已經存在的排序
2.減少Comparison
所以已經排好的資料
本身就是一個 run
排序結束
But...
[... 6 7 8 9 10 1 2 3 4 5 ...]
當我們要合併兩個 run
看起來很漂亮可以接在一起
run1 = [1 2 3 4 5]
run2 = [6 7 8 9 10]
Merge Sort說
我們來比較第一個元素吧
run1 [1 2 3 4 5]
run2 [6 7 8 9 10]
結果 = [1]
run1 [1 2 3 4 5]
run2 [6 7 8 9 10]
結果 = [1 2]
run1 [1 2 3 4 5]
run2 [6 7 8 9 10]
結果 = [1 2 3]
run1 [1 2 3 4 5]
run2 [6 7 8 9 10]
結果 = [1 2 3 4]
run1 [1 2 3 4 5]
run2 [6 7 8 9 10]
結果 = [1 2 3 4 5]
...可是...
run1 = [101 ... 200]
run2 = [1 2 3 ... 100]
100 次比較
Gallop (v.) 馳騁
// Tim真的很愛運動...
俗稱
倍增搜尋法
Exponential Search
A = [101 102...200]
B = [1 2 3 4 5...100]
A[0] > B[0]
A[0] > B[1]
A[0] > B[3]
A[0] > B[7]
A[0] > B[15]
...
A[0] > B[2**n-1]
A[0] <= B[2**(n+1)-1]
A[0] > B[2**(n-1)-1]
A[0] <= B[2**n-1]
left = 2**(n-1)-1
right = 2**(n)-1
Binary Search
直接做Binary Search?
run1 = [1 3 5 7 9...2N+1]
run 2 =[0 2 4 6 8...2N]
O(N) -> O(NlogN)
合併run的順序?
把發現的Run丟進Stack
保證最後三個元素
[..... A B C]
A > B+C
B > C
考慮極端的狀況
A = B + C
[... 64 32 16 8 4 2 1]
2^n 成長!!
常出現在 run 的長度很短的Case!
1. Stack不會太大
Python意外地注重空間使用...
2. 必免規模差太多的run馬上合併
Merge會需要大量移動,效益又不高
最爛差不多
Merge Sort 的複雜度
Conclusion
利用已存在的遞增run
我相信真實世界的資料不會完全是Random的
有效率的合併沒有交叉的runs
Exponential Search 同時保證 linear 跟 Binary 的效率
用stack保持run的長度指數成長
(同時保證效率跟Stack大小)
...偷偷省略的部分...
1. 當找到的run太小的話
合併太小的run會讓Compare增加
小範圍做Binary Insertion Search
然後還要討論怎樣算太小...
2.一開始不會直接做gallop
如果資料是random的
一直用gallop會輸給linear search