• 沒有找到結果。

Divide & Conquer

N/A
N/A
Protected

Academic year: 2022

Share "Divide & Conquer"

Copied!
71
0
0

加載中.... (立即查看全文)

全文

(1)

Algorithm Design Methods Divide & Conquer

by Yuan

(2)

從一個例子開始…

• 占三格的L形方塊

• 是否可以不重疊的放入8x8,且缺了一格的棋盤中呢?

我們來試試看…

很「貪心」的盡量 把每個方格塞滿

糟糕了…好像行不通

怎麼辦?難道要回 去窮舉每一種可能 嗎?

(3)

別緊張!!

• 遇到看似不可解的問題怎麼辦?

• 大問題不會解,小問題總會解了吧?

• 不妨從較小規模的合理問題開始思考!

• 圖一是否可解呢?看起來易如反掌

• 放大一倍試試看!

• 圖二是否可解呢?看起來沒那麼難……

• 要怎麼從小問題的解法獲得一些線索呢?

圖一

圖二

(4)

切割!

• 這樣一來,就很像解四次圖一的 問題了!

• 可是有個小小的插曲:其中的三 格並沒有缺格

• 我們正好可以放上一塊方塊完美 的解決這個問題!

• 產生四個規模較小的子問題~

(5)

遞迴過程

X 3

X4 X4

問題夠小,我們會解了!

邊長=2為終止條件

(6)

什麼是「分治」?

• 切割問題,然後征服問題!

• 把問題切割成相似的子問題

• 利用相似的方法解決它

• 剛剛的例子,由「分治法」構造出一組正確的方案

• 遞迴是方便實做分治想法的好工具

• 遞迴的本質是用堆疊保存每一層函數之區域變數的狀態

(7)

且慢,貪心不好嗎?

• 貪心很棒啊,總是拿目前最有利的解

• 剛剛的問題,不知道該怎麼貪心

• 也有些問題,太貪心,得不償失

• HW5, Problem 1

• 總是以面額較高之貨幣付款,能讓使用的貨幣數量最小化嗎?

• 如果硬幣的面額是 {1,2,4,8}, 想要湊出面額15, 怎麼做?

• 如果硬幣的面額是 {1,500,501}, 想要湊出面額1000, 怎麼做?

(8)

那,窮舉總行了吧

• 太過曠日費時,天荒地老

• 盲目的窮舉並沒有好好的利用問題的性質

• 剛剛的問題,如果對於每一個方格,窮舉四個方向

• 總複雜度需要 O(4^N) !!

• 其中, N是擺放的L型數量

(9)

• 直接解決大問題很難…

• 要是問題滿足以下條件:

• 規模小的時候, 輕鬆簡單, 易如反掌

• 規模大的時候, 既不能貪心, 窮舉又不切實際……

幸好,我們可以把大問題切割成小問題

• 而且,小問題的答案有助於我們探尋大問題的答案!

• 就可以考慮使用分治的概念解題

(10)

分工合作的想法

• 把大問題變成一些相似的小問題

• 該怎麼變, 術語叫”切割問題”

• 不一定會有多個子問題,可能一個就足夠

• 有些不可能對答案造成影響的子問題,可以直接忽略

• 把小問題算出答案 (怎麼算的不重要, 算得出來就好)

• 就是”遞迴求解”囉

• 只要問題的切割有遇到終止條件的一天, 一定算得出來!

• 把小問題的答案變成大問題的答案

• 術語叫”合併問題”

• 善加利用我們已經得到的特性

(11)

複習一下二分搜尋法

• 在S中搜尋某數

• 不可能對答案造成影響的子問題,可以直接忽略

• 於是拋棄不用求解的子問題

• 分割問題為兩個規模接近的子問題,子問題的規模是原問題 的一半

• 只有分,不太需要治

• 因為最後都只有一個可能的分支

• 子問題的解直接就會是答案

a M1 b M c M2 d

S

a M1 b M c M2 d

S

(12)

適用分治的時機

• 天生就適合分治的問題

• 問題本身就長得很遞迴 (原問題可以切割成許多規模較小的問題)

• 資料結構由遞迴定義而得

• 用分治的想法可以讓程式很清楚!

我是Heap!

我也是Heap!

我是二元搜尋樹!

我的子孫也是!

(13)

遞迴定義

f()= =

(14)

適用分治的時機

• 看起來貌似沒有分治結構的問題,如剛剛的L型方塊

• 但分治可以幫助我們求解

• 再舉一個例子

• 輸入n,請構造出一組1~n的排列,滿足任意選擇其中三個數,按 照原本的順序排列,均不會形成等差數列。

• n=8

• 3 4 2 1 7 8 5 6

(15)

• 想要直接構造長度為n的解答,似乎很困難?

• 不妨考慮分治:如果我們在已有長度為n/2的解答X的情況下,是 否有辦法構造出一組長度為n的解答X’呢?

• 既然解答X合法,表示從X中任意取三個數,必定不會形成等差數 列

• 如果對於X中的每個元素加減一個數,是否還保持此性質?

• 如果對於X中的每個元素乘上一個數,是否還保持此性質?

(16)

• N=3 的解:3 1 2

• N=6 的解:要如何構造,使得1~6都會用到呢?

• 使用N=3 的解做一些變化,但避免變化前與變化後形成等差數列

• [3 1 2] 每個數值都加上3,得到 [6 4 5]

• [3 1 2] 與 [6 4 5] 組合,這樣可以用到每個數

• [3 1 2 6 4 5]

• 似乎並未有效的防止等差數列產生

• 換個想法,利用乘法得到較大的數

(17)

於是乎

• [3 1 2] 每個數值都乘以2,得到 [6 2 4]

• 剩下 1, 3, 5 不妨把[6 2 4]各減1,得到 [5 1 3] 這樣可以用到每個數

• 該怎麼決定他們的位置呢?

• 如果把奇數都擺在偶數的前面,無論是 [奇奇偶] 或 [奇偶偶]都不可能形成 等差數列!當然,因為遞迴的性質,[奇奇奇]與[偶偶偶]也不可能

我們成功找到一種由 N=3 的解 構造出 N=6 的解 的方法了!

• 有了這個思路後,剩下的想法就容易許多,不妨自己試試看,如果n 是奇數的話,也可以這樣做嗎?

(18)

那那…

• 以上都是如果不把問題規模縮小,我們難以構造的問題

• 對於原本我們就會解答的問題

• 分治法有沒有任何幫助呢?

• 給定a,n,M (int範圍內),請求出 (a^n)%M

• 還記得嗎~~ (a*b)%M = ( a%M * b%M ) % M

• 因此,提前取餘數並不會造成結果不同,

• 於是我們在此不用擔心溢位問題

(19)

• Ans = (a^n)%M

• 用迴圈求解

• for(ans=1,i=0;i<n;i++)

• ans=ans*a%M;

• 時間複雜度為 O(n)!

• 可是n在int範圍內,這樣不夠快

• 其實,我們隱含著把求解「a^n」分割成求解「a^(n-1)」與

「a^1」的兩個子問題,再利用相乘合併,求得「a^n」的答案

• 兩個子問題的規模懸殊太大,並不是個理想的分割方法

(a^n)%M

(20)

換個想法

• 在生活中,我們如何計算2^16呢?

• 2*2=4

• 4*4=16

• 16*16=256

• 256*256=65536

• 只要四次就足夠了

• 那2^17呢?

• 再多乘一次2就好!

(21)

• Divide (把問題分隔成相似的小問題)

• a^n =

• If n%2 == 0, a^n = [a^(n/2)]^2

• If n%2 == 1, a^n = [a^((n-1)/2)]^2*a

• a^(n/2)確實是「性質相近」且「規模較小」的子問題

• Recursive (求出小問題的解答)

• 終止條件:If n = 1, 則 a 就是解答了!

• Conquer! (Merge 利用小問題的答案求解大問題)

• 設 b = a^(n/2), 則 If n%2 == 0, a^n = b*b, else a^n=b*b*a, 都是常數時間可以完成的工作!

(22)

分析一下時間

• 直觀的看來,每次可以約略把 n 的大小變為一半或更小

• 直到 n 變成 1 為止

• 從大問題到子問題的規模:n -> n/2 -> n/4 -> …… -> 1

• 總共有 (int)log_2(n)+1 層,每一層的合併時間為常數

• 因此,我們可以在 O(log n)時間內求出解答

• 盡量把問題分為兩個大小約略相等的子問題,讓子問題的最大規 模盡量快速的變小,才能有效率的降低複雜度

• 思考:如何快速的計算 (a + a^2 + a^3 + … + a^n) 呢?

(23)

常見的排序問題

• 插入排序法

• 泡沫排序法

• 選擇排序法

• 時間複雜度 = ?

(24)

合併排序法 Merge Sort

• 現在想要排序一個陣列 長度為 n 的陣列

• 依樣畫葫蘆

• 分割: 把陣列分成左右長度差不多的兩部分

• 遞迴: 把左右兩部分都各自排序完成

• 合併: 把左右部分排序後的結果合併起來,成為大問題的答案

• 想一想:給定兩個已經排序過的陣列A,B, 有沒有可以快速得到 A與B所有元素一起排序過後的結果的方法呢?

(25)

由兩個分別排序過的陣列 合併出整體的結果

• A = [1 5 6 8 9] B = [2 4 7 10]

• C[1] = 目前 A 跟 B 當中最小的元素

• 最小的在哪裡? 只可能在A的開頭或B的開頭

• C[1] = 1

• A = [1 5 6 8 9] B = [2 4 7 10]

• C[2] = 2

• A = [1 5 6 8 9] B = [2 4 7 10]

• C[3] = 4

• A = [1 5 6 8 9] B = [2 4 7 10] ……..

• C[4] = 5

假設兩部分都已經排序完成, 該怎麼合併呢!?

(26)

繼續

A = [1 5 6 8 9] B = [2 4 7 10] => C[5] = 6

A = [1 5 6 8 9] B = [2 4 7 10] => C[6] = 7

A = [1 5 6 8 9] B = [2 4 7 10] => C[7] = 8

A = [1 5 6 8 9] B = [2 4 7 10] => C[8] = 9

A = [1 5 6 8 9] B = [2 4 7 10] => C[9] = 10

A = [1 5 6 8 9] B = [2 4 7 10] => 完成!

C = [1,2,4,5,6,7,8,9,10]

(27)

合併需要幾次運算

• 每次從A的開頭與B的開頭, 挑選數值較小者

• 如果是空的就忽略他

• 決定C的每一項, 只需要一次比較 => O(1)

• 每次合併所需時間:該部分的數列長度

• A = [1 5 6 8 9] B = [2 4 7 10]

• C = [1,2,4,5,6,7,8,9,10]

• 在這個例子中長度是 9

(28)

遞迴樹

• 雖然看不見,但實際上我們求解問題的過程可以畫成一 個樹狀結構,我們稱之為「遞迴樹」,樹上的每個節點 實際上代表每個子問題

• 不同於以往,貪心法是「直接走向最佳解所在的分支」,

並得以證明(至少一組)最佳解在該分支中;

• 現在行不通了,必須「綜合考量可能分支所得到的解」,

然後藉由這些小問題的解,找出原本大問題的解!

(29)

29

Merge sort ~

7 2 9 4  3 8 6 1

(30)

30

Merge sort ~

• 求左半邊

7 2  9 4

7 2 9 4  3 8 6 1

(31)

31

• 左半邊的左半邊

7 2  9 4

7  2

7 2 9 4  3 8 6 1

(32)

32

Merge sort ~

• 到”終止問題”囉, 剩下一個!

7 2  9 4

7  2

7 7

7 2 9 4  3 8 6 1

(33)

33

• 繼續做

7 2  9 4

7  2

7 7 2 2

7 2 9 4  3 8 6 1

(34)

34

• 合併!

7 2  9 4

7  2 2 7

7 7 2 2

7 2 9 4  3 8 6 1

(35)

35

• 合併!

7 2  9 4

7  2 2 7 9 4 4 9

7 7 2 2

7 2 9 4  3 8 6 1

9  9 4 4

(36)

36

• 繼續合併

7 2  9 4 2 4 7 9

7  2 2 7 9 4 4 9

7 7 2 2 9  9 4 4 7 2 9 4  3 8 6 1

(37)

37

• 右邊也做一做

7 2  9 4 2 4 7 9 3 8 6 1 1 3 6 8

7  2 2 7 9 4 4 9 3 8 3 8 6 1 1 6

7 7 2 2 9  9 4 4 3 3 8 8 6 6 1 1 7 2 9 4  3 8 6 1

(38)

38

• 完成囉!

7 2  9 4 2 4 7 9 3 8 6 1 1 3 6 8

7  2 2 7 9 4 4 9 3 8 3 8 6 1 1 6

7 7 2 2 9  9 4 4 3 3 8 8 6 6 1 1 7 2 9 4  3 8 6 1 1 2 3 4 6 7 8 9

(39)

分析一下執行時間

長度為 n/2 長度為 n/2

長度為 n/4 長度為 n/4 長度為 n/4 長度為 n/4

長度1 長度1 長度1 長度1 長度1 長度1 長度1 長度1

長度為 n

第一層總和: 1個節點, 每個長度O(n)

第二層總和: 2個節點,每個長度約(n/2), 總和 O(n)

第三層總和: 4個節點,每個長度約(n/4), 總和 O(n)

第四層總和: 8個節點,每個長度約(n/8), 總和 O(n)

第 log n 層總和: n個節點,每個長度1, 總和 O(n)

(40)

每一層的時間都是 O(n) !

• 終止條件為n=1, 總共有幾層呢?

• 顯然由一些簡單的數學, 會發現共有 log N 層!

• 總時間 O(n log n) !!

• 空間呢? 我們只要利用原本的陣列操作就可以囉!

• 需要一個輔助陣列暫存合併後的結果

• 不可以覆蓋到原本的兩個要合併的陣列

(41)

Merge-sort function

• N、A[0~(N-1)]

• mergesort(0, N):  其中包含Left, 不包含Right

• Example: mergesort(3, 7) => 排序 A[3], A[4], A[5], A[6]

mergesort(Left, Right):

if(Left+1==Right) return;  長度為1, 終止條件 int Mid = (Left + Right) / 2;  找出中間的位置

mergesort(Left, Mid);  遞迴求解, 排序好 A[Left], …, A[Mid-1]

mergesort(Mid, Right); 遞迴求解, 排序好 A[Mid], …, A[Right-1]

int L=Left, R=Mid, K=Left;

while(L<Mid || R<Right)  開始合併 左半邊開頭 = L, 右半邊 = R if( L<Mid && (R>=Right || A[L]<=A[R]) )

// 若左半邊還有東西 , 且 (1)右半邊空了 (2)左半邊的開頭較小 B[K++] = A[L++];  放進左半邊的開頭, 而開頭位置L加1

else B[K++] = A[R++];  放進右半邊的開頭, 而開頭位置R加1 for(L=Left; L<Right; L++)  丟回去原本的陣列

A[L]=B[L];

(42)

另外的一種思路

• 快速排序法

• 對問題先好好的分割,讓合併時不那麼麻煩!

• 分割:

• 從陣列中選一個值 x

• 亂排一下, 把陣列分成三個區域 順序保持 L; M; R

• L: <x的元素 (順序不重要)

• M: =x的元素

• R: >x的元素 (順序不重要)

• 遞迴: 用快速排序法分別把 L 跟 R 排好

• 合併: 把左右各自的結果合併起來

• 怎麼合併? 不用合併,遞迴完成自然而然整個都是遞增的

(43)

快速排序法~

• 選擇 x 值 = 6, 讓數列滿足

• 在此為了講解方便, 三個部份我們依照原本的順序排列

• 這步有很多種不同的實作方式

• (L) 7 2 9 4 3 7 6 1 (R)

• 反覆進行以下操作:直到 L>=R 的位置為止

• 從L往右找第一個>=6的數 p

• 從R往左找第一個<=6的數 q

• 交換 p,q ,L往右移一格,R往左移一格

• 7

(L) 2 9 4 3 7 6 (R)

1

• 1 2 9 (L) 4 3 7 (R)

6

7

• 1 2 6 4 3 7

9

7

• 在此為了講解方便,我們假設結果為

[2 4 3 1] 6 [7 9 7]

7 2 9 4 3 7 6 1  2 4 3 1 6 7 9 7

小於6 等於6 大於6

(44)

快速排序法~

• 開始排序, 左右部分已經分好囉

7 2 9 4 3 7 6 1  2 4 3 1 6 7 9 7

(45)

快速排序法~

• 遞迴排好左半邊

2 4 3 1  1 2 4 3

7 2 9 4 3 7 6 1  2 4 3 1 6 7 9 7

(46)

快速排序法~

• 遇到終止條件

2 4 3 1  1 2 4 3

1

7 2 9 4 3 7 6 1  2 4 3 1 6 7 9 7

(47)

快速排序法~

• 1 的順序確定了

7 2 9 4 3 7 6 1  2 4 3 1 6 7 9 7

2 4 3 1  1 2 4 3

1

(48)

快速排序法~

• 排右半邊

7 2 9 4 3 7 6 1  2 4 3 1 6 7 9 7

2 4 3 1  1 2 4 3

1 4 3  3 4

4

(49)

快速排序法~

• 3 與 4 的順序確定了, 由於原數列滿足

• 所以兩個部分各自排好後, 整體就滿足從小到大的關係

7 2 9 4 3 7 6 1  2 4 3 1 6 7 9 7

2 4 3 1  1 2 3 4

1 4 3  3 4

4

(50)

快速排序法~

• 左半邊都排序完成了

7 2 9 4 3 7 6 1  1 2 3 4 6 7 9 7

2 4 3 1  1 2 3 4

1 4 3  3 4

4

(51)

快速排序法~

• 開始做右半邊

7 9 7 1  1 3 8 6 7 2 9 4 3 7 6 1  1 2 3 4 6 7 9 7

2 4 3 1  1 2 3 4

1 4 3  3 4

4

(52)

快速排序法~

• 小於7的部分是空的, 不需要遞迴求解

7 9 7  7 7 9 6 7 2 9 4 3 7 6 1  1 2 3 4 6 7 9 7

2 4 3 1  1 2 3 4

1 4 3  3 4

4

9

(53)

快速排序法~

• 更新回去 (雖然只有一個元素)

7 9 7  7 7 9 6 7 2 9 4 3 7 6 1  1 2 3 4 6 7 9 7

2 4 3 1  1 2 3 4

1 4 3  3 4

4

9

(54)

快速排序法~

• 兩個部分都做好了, 大功告成 @_@

7 9 7  7 7 9 6 7 2 9 4 3 7 6 1  1 2 3 4 6 7 7 9

2 4 3 1  1 2 3 4

1 4 3  3 4

4

9

(55)

分析時間

• 每次需要比較一個元素與x的大小關係, 決定他的位置在左邊還是右邊

• 所需時間與該部分元素數量呈線性關係

• 每一層總和都接近 O(n)等級

(56)

長度為 n/2 長度為 n/2

長度為 n/4 長度為 n/4 長度為 n/4 長度為 n/4

長度1 長度1 長度1 長度1 長度1 長度1 長度1 長度1

長度為 n

第一層總和: 1個節點, 每個長度O(n)

第二層總和: 2個節點,每個長度約(n/2), 總和 O(n)

第三層總和: 4個節點,每個長度約(n/4), 總和 O(n)

第四層總和: 8個節點,每個長度約(n/8), 總和 O(n)

第 log n 層總和: n個節點,每個長度1, 總和 O(n)

好的情況

(長相跟Merge sort一模一樣)

(57)

分析時間

• 在一般的情形下 (x介於中間, 使得L與R的長度差不多) 也只會有log n層, 於是在這種情形下的時間複雜度與 Merge sort相等

• O(n) * O(log n) = O(n log n)

• 還記得演算法的執行時間中, 除了時間複雜度(執行時間 與問題規模的相對增長速率)以外, 執行時間的常數也會 稍微影響執行速度

快速排序法在好的情況略比Merge Sort快

(58)

糟糕的情況

• 長度總和n

• 長度總和n-1

• 長度總和n-2

• 長度總和n-3

• …………

• 長度總和1

• 1 + 2 + 3 + … + n = n(n+1)/2 = O(n^2) !!

(59)

所以……

• 總共可能高達n層!!!

• 最差情況: O(n) * O(n) = O(n^2) 遠大於 O(n log n)

• 幸運的是, 這並不常發生

• 邪惡的出題者表示: 依據莫非定律, 你覺得 不會發生的情況有時特別容易發生OAOAOAO

(60)

比較一下兩種排序的方法

• 合併排序:

• 隨意分割問題(左右切一半), 然後好好的合併

• 因為分割的兩個問題大小接近, 所以遞迴的深度不會超過 O(log n), 總時間 O(n log n).

• 快速排序: (聽起來很迅速)

• 好好的分割問題(排列好大小關係), 然後自然就合併了

• 因為分割的兩個問題大小可能差異很大, 所以遞迴的深度高 達O(n), 總時間最差 O(n^2), 平均來說 O(n log n).

• 我們可以隨機的選取中心點, 這樣期望複雜度為O(n log n)

(61)

經典問題 – 逆序數對數量計算

• A = [2, 4, 1, 3]

• 對於一個陣列 A 中, 任取兩個元素, 順序不變, 如果前者大於後者, 則我們稱為”逆序數對”

• (2,4)

• (2,1) => 逆序數對

• (2,3)

• (4,1) => 逆序數對

• (4,3) => 逆序數對

• (1,3)

(62)

計算逆序數對的數量

• 既然不知道答案, 就每一種組合都試試看!

• 還記得傳說中的枚舉法

• 枚舉所有數對…檢查一下大小就好!

• 有 n(n-1)/2 = O(n^2)種!

• 成本太高囉

• 仔細想想, 我們並不是真的需要找出所有逆序數對

(63)

分治!

• 我們試著把陣列分成兩半

• 線條就是”逆序數對”

• 分成兩半後有兩種情形

• 1. 在同一部分 (上面的線條)

• 2. 在不同部分 (下面的線條), 橫跨兩邊

7 2 9 4 3 8 6 1

(64)

分割問題

• 1. 在同一部分 => 遞迴求解

• 2. 在不同部分 => 合併的時候處理

7 2 9 4 3 8 6 1

(65)

遞迴形式

• 因為分割問題的方法與 Merge Sort 非常類似,因此就沿用一 樣的定義吧!

(66)

寫成函數

N、A[0~(N-1)]

int counting(Left, Right):  其中包含Left, 不含Right

回傳逆序數對的數量

N=8, A[0~(N-1)] = [7,2,9,4,3,8,6,1]

counting(0,4) = 3

counting(5,8) = 4

Mid=(Left,Right)/2

counting(Left,Right)=

counting(Left, Mid) (都在左邊)+counting(Mid, Right) (都在右邊)

橫跨左右部分的逆序數對 (下面的線條)

7 2 9 4 3 8 6 1

(67)

合併問題

• counting(Left,Right)=

• counting(Left, Mid) (都在左邊)+counting(Mid, Right) (都在右邊)

• 橫跨左右部分的逆序數對 (下面的線條)

• 橫跨左右部分的逆序數對: 怎麼求呢?

• 枚舉左邊的每一項,右邊的每一項

• 在第一層就 O(n/2) * O(n/2) = O(n^2) !

• 辛苦的分治白忙一場, 問題的合併變成瓶頸

• 有沒有更好的合併方法呢

(68)

觀察性質

• 觀察1: 因為這些數對橫跨兩邊, 因此:

• 即使將左右兩部分排序, 這部分的數量依然相同!

• 觀察2: 經過排序以後瓊舉左邊的每一項, 與它 有關的逆序數對是原本右邊滿足的部分, 加上往 右找比它小的那些數值!

7 2 9 4 3 8 6 1

2 4 7 9 1 3 6 8

1對: (2,1)

2對: (4,1)/(4,3) 3對

4對共有10對!

3>1, 停

6>4, 停

(69)

時間呢

• 回傳: 左半部分與右半部分(排序前)的逆序數對數量: 3+4

• 加上上面所有箭頭的長度和: 10 = 3+4+10 = 17

• 每瓊舉左邊的一項x, 就只要檢查上一次瓊舉的部分箭頭終點的下一 項y, 如果y<x, 那箭頭就可以延伸!

• 設該層共有n個元素, 左右各n/2個

• 左邊枚舉n/2次

• 箭頭也至多只會延伸n/2次

• O(n) !!

• 與合併排序法的時間分析一模一樣

2 4 7 9 1 3 6 8

(70)

寫寫程式吧

• 以合併排序法為基礎

• 在合併前額外加上計算 “橫跨左右部分的逆序數對數量”

• 回傳三個部份的總和, 就是在處理範圍中的逆序數對數量

• 順便也幫忙排序完成了!

• 在此問題中,子問題的答案並不只有「該區間內逆序數對的數 量」,也隱含了「該區間內排序過的結果」

(71)

counting function

• N、A[0~(N-1)]

• int counting(Left, Right):  其中包含Left, 不含 Right

• 回傳逆序數對的數量

int

counting (Left, Right):

if(Left+1==Right) return 0;  長度為1, 終止條件 int Mid = (Left + Right) / 2;  找出中間的位置

int Cnt = counting(Left, Mid);  遞迴求解, 順便排序A[Left]~A[Mid-1]

Cnt += counting(Mid, Right); 遞迴求解,順便排序A[Mid]~A[Right-1]

int L, R=Mid;  R: 箭頭的起點永遠在中間

for(L=Left; L<Mid; L++){  枚舉左邊的每一項

while(R<Right && A[R]<A[L])  如果箭頭可以延伸的話 R++;  就延伸吧!

Cnt += R-Mid;  加上箭頭的長度 }

//Merge sort: 合併左右兩個部分 return Cnt;

參考文獻

相關文件

Algorithm Design Methods Greedy Algorithm.. by Chin

Algorithm Design Methods Greedy Algorithm.. by Chin

Piecewise polynomial interpolation: divide the interval into a collection of subintervals and construct different approximation on each subinterval. The simplest piecewise

Algorithm Design Methods Enumeration.. by Chin

if left_sum&gt;=right_sum and left_sum&gt;=cross_sum return (left_low,left_high,left_sum). else if right_sum&gt;=left_sum and right_sum&gt;=cross_sum

mid: 左半部 array 的最大 index high: array 最大的 index.. 股市大亨 之

Let and be constants, let be a function, and let be defined on the nonnegative integers by the recu rrence. where we interpret to mean either

證明比較鬆的upper bound或lower bound來慢慢 接近tight