雜湊排序法

2021-04-12 17:42:29 字數 2440 閱讀 1507

排序法總體上可以分兩大類,一類是基於『比較』的,主要有大家非常熟悉的:選擇排序,交換排序,插入排序,歸併排序等,其演算法的複雜度也是用『比較』的次數衡量的,其中有非常高效和優秀的『快速排序』,可以說是他們中間的佼佼者,無論從時間還是空間上說都有很好的效能;另外一類也就自然是不基於『比較』的,《資料結構》上介紹過一種叫『基數排序』,我覺得也很經典,今天我要向大家介紹的跟基數排序很類似,原理也非常簡單。

和基數排序的方法非常類似,也是採用分配和收集,而我管它叫『雜湊』,因為就和hash雜湊表一樣,而且我可以說當資料比較均勻的離散分布的時候,效率是非常高的,可以花很少的幾次雜湊就得到有序結果。

[寫在前面,也跟基數排序一樣只適用於整數排序,因為不基於比較就只能從元素,即數的本身出發了。]

先舉個例子,設待排序的數是:

4,2,5,1,3

我們排序的任務就是讓上面一列數有序,看看我們要做的結果是什麼:

1,2,3,4,5

於是我們的任務就是,把前面一列數各位數放到『正確』的位置上去,使得它有序。而乙個數和它的正確位置就是乙個對映關係,於是我們就是要找到乙個函式f(x),使f(x)->『x的正確位置』!

上面的例子非常簡單,f(x)=x,但實際情況非常複雜,其實像這樣的f(x)是不可能有很好的數學表示式的,別指望在這裡做更多的努力。於是我著手找近似的f(x),我的想法是,只要讓它不會錯太多就行了,不完善的可以慢慢做得完善。

實際上f(x)就是乙個雜湊函式,只不過它不是用來檢索而是用來排序,我給出乙個f(x)=[(x-min)*(n-1)/(max-min)],其中min是這列數的最小值,max是這列數的最大值,n是這列數的個數,那值域就會在[0,n-1],再用這些值找雜湊儲存位置。

在這樣乙個近似的f(x)函式下,會出些『意外』的情況,首先可能會有兩個元素得到相同的f(x)值,也就是『衝突』,衝突如何解決?可以採用拉鍊法,類似於hash表的衝突處理。

為什麼要用拉鍊法,其實可以從集合的角度看這個演算法,實際上我是把整個數列看成乙個集合,然後我著手把它分成更小一些的子集,同時使這些子集『集合間有序』,所謂集合間有序是指:

如果 集合a《集合b

那麼 對於任意的x,x屬於a,又對於任意的y,y屬於b,都成立x然後不斷往下分,直到被分的集合中只含有乙個元素為止,排序工作就完成了。

再來個例子:

1,4,3,7,3,2

於是min=1,max=7,n=6

f(x)=[(x-1)*5/6]

第一次劃分的結果是:

收集鍊錶id:0 1 2 3 4 5

[元素]      1 3 4     7

2 3就得到了四個子集,設對應id的子集記為[id],那麼它們是

(它們也是第一次劃分f(x)的值域)

它們是『集合間有序』的。

演算法到此全部描述完了,另外我還想說點具體實現的過程,雖然沒源**了,還有些細節問題。

1、f中的min,max該如何求?

給我們的排序任務,只給出資料位址和資料元素,並沒有給出min和max,是不是需要先求一下呢?如果要求一下,又回到了比較的方法,於是整個演算法就不是很好的不基於比較的演算法。我的看法是,具體排序的時候如果能給出來可以盡量給出(比如,按乙個城市所有人的年齡排序,範圍可以是0~150),如果真給不出來,沒關係,我們取那種型別的資料的最小值和最大值。但是這不會影響到效率嗎?後面再討論效率。

另外再提出個概念,叫『區分度』,區分度就是指一次劃分的時候,得到的子集內的元素的最大值和最小值之差的最大值;這樣吧,可能不好理解。這樣記,我記一次劃分為:

div(min,max):(這樣記是因為,一次劃分是跟min和max分不開的,確定了min和max才能劃分)

-->

那這次劃分的區分度就是

max在前面的例子中就是1,因為[0]中2-1最大。

為什麼要提這個概念,在你劃分一次後如果沒有得到結果,那必須再對子集進行劃分,這時的子劃分中的max-min正是前一次的區分度。如果還知道min,那子劃分就確定了。而min是可以線性求出來的,於是子劃分就完全確定下來了。

區分度約為(max-min)/n *

2、這個演算法的效率如何?

首先從時間上考慮。整個排序的過程可以看成是把乙個有n個元素的集合分成n個只有乙個元素的子集的過程。而就以劃分的次數來衡量的話,最差的情況是,連續幾次劃分都沒有分成功,原有集合本身做為子集,但是這樣的情況不會持續很久,隨著更加細緻的劃分,區分度會不斷變小,區分度的迭代遞推式是:d'=d/n,實際上是非常快速的收斂。其次,如果每一次劃分只能分成兩個子集,效率會如何呢?注意到劃分出的子集是集合間有序的,這會聯想到快速排序的一次劃分,沒錯快速排序的一次劃分能且只能將元素劃分成兩個部分,這兩個部分有序,所以應該和快速排序的效率相當。最後,如果每一次劃分能分成兩個子集以上,效率會更高嗎?不用我說了,應該會更高吧~~

實際上對於離散分布比較均勻(等概率分布)的無序數,排起序來是非常高效的~!

另外從空間上考慮。這就比快速排序差太多了,整個輔助空間需要得很大,基本上需要和原有資料量一樣多的空間,應該是o(n),當n很大時,執行的實際耗時基本上都花在記憶體空間分配和**上了。

雜湊衝突之雜湊法

雜湊碰撞 雜湊衝突 不同的key值經過雜湊函式hash key 處理以後可能產生相同的值雜湊位址,我們稱這種情況為雜湊衝突。任意的雜湊函式都不能避免產生衝突。閉雜湊法 線性探測 void insert1 int x pos if pos v.capacity s pos exist v pos x ...

雜湊拉鍊法(雜湊桶)

昨天寫了雜湊的開放定址法的部落格,今天我們要說的是拉鍊法,還有乙個名字叫雜湊桶。雜湊桶要做的就是,之前我們用的開放定址法,通過將資料對映到陣列中來實現雜湊。這裡每個陣列的位置只能存放乙個資料,如果衝突的話會繼續往下找找到空的位置然後放進去,但是其實大家都能感覺出來上乙個 很簡單,也很扯,感覺實現起來...

雜湊 雜湊法 LeetCode例題

說到雜湊,總感覺到它很高深,說到底它只不過是實現集合和字典 滿足資料增刪改查的結構 的一種方式,而不是總把它當成單獨的一種資料結構或者演算法去討論。定義 將資料中key傳給乙個雜湊函式f 按照計算出來的雜湊值h f key 將那個帶有key的資料分布到乙個雜湊表中 大小m自定義 當m 資料量n的時候...