對於那些熟悉合併排序的人,我試圖找出合併兩個大小爲n/2的子數組所需的最小比較次數,其中n是原始未排序數組中的項目數。我知道該算法的平均和最壞情況的時間複雜度是O(nlogn),但我無法弄清楚所需的比較數量(根據n)的確切數量。使用合併排序算法所需的最少比較次數?
回答
合併步驟的最小比較次數大約爲n/2
(順便說一句,仍然是O(n)
),假設一個列表中的其中一個已經完全遍歷,那麼假設一個合理的實現。例如,如果兩個已有效排序的列表正在被合併,則將較大列表的第一個成員與較小列表進行比較n/2
直到它被用盡;那麼可以複製較大的列表而無需進一步比較。
List 1 List 2 Merged List Last Comparison
[1, 2, 3] [4, 5, 6] [] N/A
[2, 3] [4, 5, 6] [1] 1 < 4
[3] [4, 5, 6] [1, 2] 2 < 4
[] [4, 5, 6] [1, 2, 3] 3 < 4
[] [5, 6] [1, 2, 3, 4] N/A
[] [6] [1, 2, 3, 4, 5] N/A
[] [] [1, 2, 3, 4, 5, 6] N/A
請注意,進行了3次比較,列表中有6名成員。
再說一次,即使在最好的情況下,合併步驟仍被有效地考慮爲O(n)
。合併排序算法的時間複雜度爲O(n*lg(n))
,因爲整個列表中的合併步驟爲O(n)
,並且劃分/合併發生在O(lg(n))
級別的遞歸中。
對於每個比較,您從兩個列表中的一個列表中排除一個元素。所以比較的數量至多是兩個列表長度的總和。正如Platinum
所示,如果達到一個數組的末尾並且另一個數組中仍有項目,則可能會更少。
所以比較的數量在n/2
和n
之間。
這個答案給出了一個確切的結果,不僅使用一些Landau symbol寫的漸近行爲。
合併長度米和Ñ的列表至少需要分鐘(米,Ñ)比較。原因是,只有當其中一個輸入列表已被完全處理時,您才能停止比較元素,即您至少需要迭代兩個列表中較小的一個。請注意,這種比較次數僅對於一些輸入而言是足夠的,所以它是最小的,因爲它假設了可能的輸入數據的最佳情況。對於最壞情況的輸入,你會發現更高的數字,即n ⌈lg n⌉ − 2⌈lg n⌉ + 1。
讓Ñ = 2 ķ是二的冪。讓i成爲合併級別,其中0≤i < k。在級i你執行2 ķ - 我 - 1合併,其中的每一個需要2個我比較。將這兩個數字相乘,可以得出2 k - 1比較,其等於n/2。總結在ķ水平的合併你NK/2 =(ñ LG ñ)/ 2比較。
現在讓n小於2的冪。假設k = 012lg n⌉仍然表示合併級別的數量。與2 k的情況相比,您現在每個級別都少了一個比較。這樣合併的總數由ķ降低,導致2 ķķ/2 - ķ =(2 ķ/2 - 1)ķ比較。但是,如果您刪除多個元素,導致n = 2 k - 2,那麼您不會減少最上面的合併數,因爲另一個列表已經是較短的合併數。這表明這裏的事情可能會變得更加困難。
因此,讓我們有一點點的演示程序,我們可以同時使用來檢查我們以前的結果,並計算比較了其他值數:
mc = [0, 0] # dynamic programming, cache previous results
k = 1 # ceil(lg n) in the loop
for n in range(2, 128):
a = n // 2 # split list near center
b = n - a # compute length of other half list
mc.append(mc[a] + mc[b] + min(a, b)) # need to sort these and then merge
if (n & (n - 1)) == 0: # if n is a power of two
assert mc[-1] == n*k/2 # check previous result
k += 1 # increment k = ceil(lg n)
print(', '.join(str(m) for m in mc)) # print sequence of comparison counts, starting at n = 0
這使您可以按以下順序:
0, 0, 1, 2, 4, 5, 7, 9, 12, 13, 15, 17, 20, 22, 25, 28, 32, 33, 35,
37, 40, 42, 45, 48, 52, 54, 57, 60, 64, 67, 71, 75, 80, 81, 83, 85,
88, 90, 93, 96, 100, 102, 105, 108, 112, 115, 119, 123, 128, 130, 133,
136, 140, 143, 147, 151, 156, 159, 163, 167, 172, 176, 181, 186, 192,
193, 195, 197, 200, 202, 205, 208, 212, 214, 217, 220, 224, 227, 231,
235, 240, 242, 245, 248, 252, 255, 259, 263, 268, 271, 275, 279, 284,
288, 293, 298, 304, 306, 309, 312, 316, 319, 323, 327, 332, 335, 339,
343, 348, 352, 357, 362, 368, 371, 375, 379, 384, 388, 393, 398, 404,
408, 413, 418, 424, 429, 435, 441
您可以在On-Line Encyclopedia of Integer Sequences中查找以發現該序列描述total number of 1's in binary expansions of 0, ..., n。這裏也有一些公式,但它們不準確(涉及一些Landau符號術語),或者它們依賴於其他一些不重要的序列,或者它們非常複雜。我最喜歡的一個表達了我上面的程序:
a(0)= 0,a(2n)= a(n)+ a(n-1)+ n,a(2n + 1 )= 2a(n)+ n + 1。 - Ralf Stephan,2003年9月13日
鑑於這些替代方案,我想我會堅持使用上面的腳本來計算這些數字。您可以刪除斷言以及與此相關的所有內容,依賴於事實a < b
,並刪除輸出以及如果將其包含到更大的程序中。結果應該如下所示:
mc = [0, 0]
for n in range(2, 1024):
a = n // 2
mc.append(mc[a] + mc[n - a] + a)
請注意,例如,對於ñ = 3你只有兩個比較。顯然這隻有在你將兩個極值元素與中值元素進行比較時才能起作用,這樣你就不必再將極值元素與另一個元素進行比較。這說明了爲什麼上述計算僅適用於最佳情況輸入。最差情況下的輸入會讓你在某個點上計算最小和最大元素,導致按照n ⌈lg n⌉ − 2⌈lg n⌉ + 1公式計算的三個比較。
- 1. 這種合併排序算法能做多少次比較?
- 2. 用字節比較排序結構的最佳排序算法?
- 3. 計數與合併排序的比較
- 4. 合併排序比較計數器
- 5. 排序算法最大限度地減少了涉及單個項目的最大比較次數
- 6. 比較使用合併排序vs選擇排序
- 7. KMP算法最佳情況下的最小比較次數是多少?
- 8. 合併排序中的比較
- 9. 用最少的x <y比較排序4個數字
- 10. 計算比較次數和每個排序算法執行的副本數
- 11. 按比較排序的最小比較
- 12. 基數排序是唯一的非比較排序算法嗎?
- 13. 使用C#計算堆排序比較
- 14. 合併排序Java算法
- 15. 選擇排序中的比較次數?
- 16. .NET中的比較次數4.5排序
- 17. 爲什麼排序比線性最小搜索算法更少地調用比較函數?
- 18. 排序算法最適合對排序數組進行排序
- 19. 計算快速排序中的比較次數
- 20. 排名比較算法
- 21. 計算排序算法的比較次數,並將其添加到java中的數組列表中
- 22. 數組排序 - 和合並 - 算法
- 23. 合併排序算法 - 倒計數
- 24. 使用空合併運算符與給出的方法比較
- 25. 比較TreeBag按發生次數排序
- 26. 合併排序計數數量的交換和比較
- 27. 與最小比較排序
- 28. 對數組進行排序所需的最少操作數
- 29. 計算排序期間進行的比較次數和移動次數
- 30. 歸併排序比較
您的答案似乎只描述一個合併操作,即將兩個已排序列表合併爲一個。你錯過了ceil(lg(* n *))遞歸級別。 – MvG 2012-09-11 06:56:17
@MvG:這不是我解釋問題的方式。 「合併兩個子陣列所需的最小比較次數」,而不是「合併所需的最小比較次數」。 – 2012-09-11 14:58:57