記數排序 & 桶排序 & 基數排序
為什麽要寫這樣滴一篇博客捏...因為一個新初一問了一道水題,結果就莫名其妙引起了戰鬥。
然後突然發現之前理解的桶排序並不是真正的桶排序,所以寫一篇來區別下這三個十分相似的排序辣。
老年菜兔的覺醒!!!
記數排序
記數排序是一種很快的排序算法,但是要很多的空間。
具體的操作:
比如說給一個這樣的數列: 6 9 3 2 3 5
我萌需要一個數組 a[i] 表示 數列中 數值為 i 的有多少個
這樣 就可以 O(n) 處理出這個數組
read(x);
a[x]++;
比如辣個栗子的a數組是這樣滴
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
a[i] | 0 | 1 | 2 | 0 | 1 | 1 | 0 | 0 | 1 |
然後這個數組有什麽用捏
可以發現,只需要枚舉 i (如栗子裏是 1≤i≤9 )
然後輸出 a[i] 個 i 後就是一個從小到大排序後的數列
for i=1 to 9 do (通常可以取數列中的最大數max,這裏取栗子的9) for j=1 to a[i] do write(i,‘ ‘);
這樣之後就會輸出 2 3 3 5 6 9
這就是記數排序(老年菜兔之前把計數排序誤以為是桶排序QAQ)
效率基本為 O(N) 但是有一個缺陷,就是當數列的值十分十分大的時候,數組就開不下了。
這個問題怎麽解決好捏,就是桶排序和基數排序辣!
桶排序
桶排序實際上是對計數排序的一些優化,他把時間又換回了一部分空間(計數排序用空間換時間)。
桶排序思想是什麽捏?
繼續舉個栗子呀 6 9 3 2 3 5 (咦好熟悉)
首先我萌要定一個值 m 這個值可以任意定,但會影響到效率。
m是幹什麽用的?
不如這樣理解一下,計數排序實際上就是用了好多好多個桶 總共 max 個
而桶排序是用了 (max/m) 個桶 所以這個 m 的含義實際上是區間範圍。
計數排序是桶排序的一種特殊情況,就是 取 m=1的時候。
這時我萌需要一個數組 a[i,j]表示 第 i 個桶中的第j個元素的數值。
(通常不用數組而是鏈表,原因是可能有這樣的數據 如數列中全都是1-3範圍的 這時數組就開不下,如果n十分大)
嗯列個表吧
這裏我萌取 m=3 (就是舉個栗子)
桶中數據的範圍 | 1~3 | 4~6 | 7~9 |
相應的i(也就是第幾個桶) | 1 | 2 | 3 |
桶內的元素 | 3,2,3 | 6.5 | 9 |
這時發現什麽捏...每一個桶裏的元素是無序的。
所以對每一個桶都做一個其他的排序,如快排。
然後排序後再把這些桶合並起來就好啦
誒???每一個桶?辣麽效率豈不是很低。
答案是否定的,相反,桶排序效率通常比快排快。
快排的平均效率為O(n log n) 而桶排捏是 O(max/m *m log m) 即 O(max log M) 假設max=n=1000000(1百萬)
快排的計算量約 23000000(2300萬) 而 桶排序如果取M=2500(既省了一點空間,又有很高的時間效率) 的計算量約 12000000(1200萬)
可見桶排序效率也比較高。
而且桶排序每個桶的排序算法還可以換為其他的不一定要快排。
桶排序的應用似乎不多,而似乎很多人搞混了基數排序與桶排序。
相比之下把基數排序誤認為是桶排序的人會更多的樣子。
所以基數排序的應用應該更廣。
基數排序
基數排序的方法更是神奇,他用到了計數排序的思想。
基數排序的操作我還是要舉個栗子...不過圖就不放了QAQ不然水的成分有點大
如:543 123 756 666 841 322 10 799 69 (終於換了個栗子,因為上一次栗子次掉了)
基數排序的操作是這樣的。
最低位(個位)為一個關鍵字,次低位是一個關鍵字...以此類推。
我萌先對最低位為關鍵字做一次計數排序。
如果依舊用桶來比喻的話,因為一個位數上只會有0-9 這些數字。
所以就是10個桶
列個表吧
桶 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
第1次操作後 桶裏的元素 |
10 | 841 | 322 | 543,123 | 756,666 | 799,69 |
然後第一次操作後再合並起來就是這樣的數列 (註意每一個桶內是無序的,都是按原來數列的位置)
10 841 322 543 123 756 666 799 69
還是一個無序的數列,接著對次低位為關鍵字做記數排序
桶 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
第2次操作後 桶裏的元素 |
10 | 322,123 | 841,543 | 756 | 666,69 | 799 |
然後第二次操作後再合並起來就是這樣的數列(註意每一個桶內依舊是無序的,都是按第一次操作後的數列的位置)
10 322 123 841 543 756 666 69 799
然後就第三低位為關鍵字做記數排序
桶 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
第2次操作後 桶裏的元素 |
010,069 | 123 | 322 | 543 | 666 | 756,799 | 841 |
然後合並
10 69 123 322 543 666 756 799 841
這時排序結束,數列有序了。
是不是很神奇捏?
為什麽要對每一位都做一次計數排序?
實際上就是改變位置。比如如果出現了 123 124 這樣的元素,在原來數列是這樣的 124 123
辣麽對最低位進行計數排序就可以變成 123 124 從而改變了位置。
好了,辣基數排序的效率?
設最大值有 d 位
約O(d*n) 的效率
而d是十分小的,相比桶排序,雖然效率變低了一點,但適用於一些數值十分大的數據。
再擴充一點基排
實際上,這裏使用了10個桶,辣麽可不可以多用些桶捏?當然可以
我萌可以使用100個桶,講最低位和次低位看成一個整體 為一個關鍵字,比如 5678 此時 以78 為一個關鍵字 56為另一個關鍵字。
然後操作是一樣的但是卻只有 d/2個關鍵字了,從而效率又快了,但空間變多了,變成了100個桶。
以此類推,還有1000 10000 ...個桶。辣如果不是整10個桶行不行,當然行,這樣的話需要把10進制數看為其他進制的數來做計數排序。具體就不細講啦~
好啦~講完啦,總結一下吧,總體來說,三個排序都是用到了計數排序的思想。
桶排序效率高,但數值過大還是無法使用,而基數排序不僅效率高了很多,而且適用數值大的數據。
老年菜兔的講解結束啦~撒花~
記數排序 & 桶排序 & 基數排序