<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          圖文詳解:7000 字哈希表總結(jié)

          共 7908字,需瀏覽 16分鐘

           ·

          2021-06-02 20:37

          今天我們來(lái)說(shuō)一種新的數(shù)據(jù)結(jié)構(gòu)散列(哈希)表,散列是應(yīng)用非常廣泛的數(shù)據(jù)結(jié)構(gòu),在我們的刷題過(guò)程中,散列表的出場(chǎng)率特別高。所以我們快來(lái)一起把散列表的內(nèi)些事給整明白吧,文章框架如下。

          說(shuō)散列表之前,我們先設(shè)想以下場(chǎng)景。

            袁廚穿越回了古代,憑借從現(xiàn)代學(xué)習(xí)的做飯手藝,開(kāi)了一個(gè)袁記菜館,正值開(kāi)業(yè)初期,店里生意十分火爆,但是顧客結(jié)賬時(shí)就犯難了,由于菜品太多,每當(dāng)結(jié)賬時(shí),老板娘總是按照菜單一個(gè)一個(gè)找價(jià)格(遍歷查找),每次都要找半天,所以結(jié)賬的地方總是排起長(zhǎng)隊(duì),顧客們表示用戶(hù)體驗(yàn)很差。袁廚一想這不是辦法啊,太浪費(fèi)大家時(shí)間了,所以袁廚就先把菜單按照首字母排序(二分查找),然后查找的時(shí)候根據(jù)首字母查找,這樣結(jié)賬的時(shí)候就能大大提高檢索效率啦!但是呢?工作日顧客不多,老板娘完全應(yīng)付的過(guò)來(lái),但是每逢節(jié)假日,還是會(huì)排起長(zhǎng)隊(duì)。那么有沒(méi)有什么更好的辦法呢?對(duì)呀!我們把所有的價(jià)格都背下來(lái)不就可以了嗎?每個(gè)菜的價(jià)格我們都了如指掌,結(jié)賬的時(shí)候我們只需把每道菜的價(jià)格相加即可。所以袁廚和老板娘加班加點(diǎn)的進(jìn)行背誦。下次再結(jié)賬的時(shí)候一說(shuō)吃了什么菜,我們立馬就知道價(jià)格啦。自此以后收銀臺(tái)再也沒(méi)有出現(xiàn)過(guò)長(zhǎng)隊(duì)啦,袁記菜館開(kāi)著開(kāi)著一不小心就成了天下第一飯店。

          下面我們來(lái)看一下袁記菜館老板娘進(jìn)化史。

          上面的后期結(jié)賬的過(guò)程則模擬了我們的散列表查找,那么在計(jì)算機(jī)中是如何使用進(jìn)行查找的呢?

          散列表查找步驟

          散列表,最有用的基本數(shù)據(jù)結(jié)構(gòu)之一。是根據(jù)關(guān)鍵碼的值直接進(jìn)行訪問(wèn)的數(shù)據(jù)結(jié)構(gòu),散列表的實(shí)現(xiàn)常常叫做散列(hasing)。散列是一種用于以常數(shù)平均時(shí)間執(zhí)行插入、刪除和查找的技術(shù),下面我們來(lái)看一下散列過(guò)程。

          我們的整個(gè)散列過(guò)程主要分為兩步

          (1)通過(guò)散列函數(shù)計(jì)算記錄的散列地址,并按此散列地址存儲(chǔ)該記錄。就好比麻辣魚(yú),我們就讓它在川菜區(qū),糖醋魚(yú),我們就讓它在魯菜區(qū)。但是我們需要注意的是,無(wú)論什么記錄我們都需要用同一個(gè)散列函數(shù)計(jì)算地址,然后再存儲(chǔ)。

          (2)當(dāng)我們查找時(shí),我們通過(guò)同樣的散列函數(shù)計(jì)算記錄的散列地址,按此散列地址訪問(wèn)該記錄。因?yàn)槲覀兇婧腿〉臅r(shí)候用的都是一個(gè)散列函數(shù),因此結(jié)果肯定相同。

          剛才我們?cè)谏⒘羞^(guò)程中提到了散列函數(shù),那么散列函數(shù)是什么呢?

          我們假設(shè)某個(gè)函數(shù)為 f,使得 存儲(chǔ)位置 = f (key) 那樣我們就能通過(guò)查找關(guān)鍵字不需要比較就可獲得需要的記錄的存儲(chǔ)位置。這種存儲(chǔ)技術(shù)被稱(chēng)為散列技術(shù)。散列技術(shù)是在通過(guò)記錄的存儲(chǔ)位置和它的關(guān)鍵字之間建立一個(gè)確定的對(duì)應(yīng)關(guān)系 f ,使得每個(gè)關(guān)鍵字 key 都對(duì)應(yīng)一個(gè)存儲(chǔ)位置 f(key)。見(jiàn)下圖

          這里的 f 就是我們所說(shuō)的散列函數(shù)(哈希)函數(shù)。我們利用散列技術(shù)將記錄存儲(chǔ)在一塊連續(xù)的存儲(chǔ)空間中,這塊連續(xù)存儲(chǔ)空間就是我們本文的主人公------散列(哈希)

          上圖為我們描述了用散列函數(shù)將關(guān)鍵字映射到散列表,但是大家有沒(méi)有考慮到這種情況,那就是將關(guān)鍵字映射到同一個(gè)槽中的情況,即 f(k4) = f(k3) 時(shí)。這種情況我們將其稱(chēng)之為沖突,k3 和 k4 則被稱(chēng)之為散列函數(shù) f同義詞,如果產(chǎn)生這種情況,則會(huì)讓我們查找錯(cuò)誤。幸運(yùn)的是我們能找到有效的方法解決沖突。

          首先我們可以對(duì)哈希函數(shù)下手,我們可以精心設(shè)計(jì)哈希函數(shù),讓其盡可能少的產(chǎn)生沖突,所以我們創(chuàng)建哈希函數(shù)時(shí)應(yīng)遵循以下規(guī)則

          (1)必須是一致的,假設(shè)你輸入辣子雞丁時(shí)得到的是在看,那么每次輸入辣子雞丁時(shí),得到的也必須為在看。如果不是這樣,散列表將毫無(wú)用處。

          (2)計(jì)算簡(jiǎn)單,假設(shè)我們?cè)O(shè)計(jì)了一個(gè)算法,可以保證所有關(guān)鍵字都不會(huì)沖突,但是這個(gè)算法計(jì)算復(fù)雜,會(huì)耗費(fèi)很多時(shí)間,這樣的話就大大降低了查找效率,反而得不償失。所以咱們散列函數(shù)的計(jì)算時(shí)間不應(yīng)該超過(guò)其他查找技術(shù)與關(guān)鍵字的比較時(shí)間,不然的話我們干嘛不使用其他查找技術(shù)呢?

          (3)散列地址分布均勻我們剛才說(shuō)了沖突的帶來(lái)的問(wèn)題,所以我們最好的辦法就是讓散列地址盡量均勻分布在存儲(chǔ)空間中,這樣即保證空間的有效利用,又減少了處理沖突而消耗的時(shí)間。

          現(xiàn)在我們已經(jīng)對(duì)散列表,散列函數(shù)等知識(shí)有所了解啦,那么我們來(lái)看幾種常用的散列函數(shù)構(gòu)造方法。這些方法的共同點(diǎn)為都是將原來(lái)的數(shù)字按某種規(guī)律變成了另一個(gè)數(shù)字。所以是很容易理解的。

          散列函數(shù)構(gòu)造方法

          直接定址法

          如果我們對(duì)盈利為0-9的菜品設(shè)計(jì)哈希表,我們則直接可以根據(jù)作為地址,則 f(key) = key;

          即下面這種情況。

          有沒(méi)有感覺(jué)上面的圖很熟悉,沒(méi)錯(cuò)我們經(jīng)常用的數(shù)組其實(shí)就是一張哈希表,關(guān)鍵碼就是數(shù)組的索引下標(biāo),然后我們通過(guò)下標(biāo)直接訪問(wèn)數(shù)組中的元素。

          另外我們假設(shè)每道菜的成本為50塊,那我們還可以根據(jù)盈利+成本來(lái)作為地址,那么則 f(key) = key + 50。也就是說(shuō)我們可以根據(jù)線性函數(shù)值作為散列地址。

          f(key)  =  a * key + b    a,b均為常數(shù)

          優(yōu)點(diǎn):簡(jiǎn)單、均勻、無(wú)沖突。

          應(yīng)用場(chǎng)景:需要事先知道關(guān)鍵字的分布情況,適合查找表較小且連續(xù)的情況

          數(shù)字分析法

          該方法也是十分簡(jiǎn)單的方法,就是分析我們的關(guān)鍵字,取其中一段,或?qū)ζ湮灰?,疊加,用作地址。比如我們的學(xué)號(hào),前 6 位都是一樣的,但是后面 3 位都不相同,我們則可以用學(xué)號(hào)作為鍵,后面的 3 位做為我們的散列地址。如果我們這樣還是容易產(chǎn)生沖突,則可以對(duì)抽取數(shù)字再進(jìn)行處理。我們的目的只有一個(gè),提供一個(gè)散列函數(shù)將關(guān)鍵字合理的分配到散列表的各位置。這里我們提到了一種新的方式抽取,這也是在散列函數(shù)中經(jīng)常用到的手段。

          優(yōu)點(diǎn):簡(jiǎn)單、均勻、適用于關(guān)鍵字位數(shù)較大的情況

          應(yīng)用場(chǎng)景:關(guān)鍵字位數(shù)較大,知道關(guān)鍵字分布情況且關(guān)鍵字的若干位較均勻

          折疊法

          其實(shí)這個(gè)方法也很簡(jiǎn)單,也是處理我們的關(guān)鍵字然后用作我們的散列地址,主要思路是將關(guān)鍵字從左到右分割成位數(shù)相等的幾部分,然后疊加求和,并按散列表表長(zhǎng),取后幾位作為散列地址。

          比如我們的關(guān)鍵字是123456789,則我們分為三部分 123 ,456 ,789 然后將其相加得 1368 然后我們?cè)偃∑浜笕?368 作為我們的散列地址。

          優(yōu)點(diǎn):事先不需要知道關(guān)鍵字情況

          應(yīng)用場(chǎng)景:適合關(guān)鍵字位數(shù)較多的情況

          ???????????除法散列法

          在用來(lái)設(shè)計(jì)散列函數(shù)的除法散列法中,通過(guò)取 key 除以 p 的余數(shù),將關(guān)鍵字映射到 p 個(gè)槽中的某一個(gè)上,對(duì)于散列表長(zhǎng)度為 m 的散列函數(shù)公式為

          f(k) = k mod p   (p <= m)

          例如,如果散列表長(zhǎng)度為 12,即 m = 12 ,我們的參數(shù) p 也設(shè)為12,那 k = 100時(shí) f(k) = 100 % 12 = 4

          由于只需要做一次除法操作,所以除法散列法是非??斓?。

          由上面的公式可以看出,該方法的重點(diǎn)在于 p 的取值,如果 p 值選的不好,就可能會(huì)容易產(chǎn)生同義詞。見(jiàn)下面這種情況。我們哈希表長(zhǎng)度為6,我們選擇6為p值,則有可能產(chǎn)生這種情況,所有關(guān)鍵字都得到了0這個(gè)地址數(shù)。

          那我們?cè)谶x用除法散列法時(shí)選取 p 值時(shí)應(yīng)該遵循怎樣的規(guī)則呢?

          • m 不應(yīng)為 2 的冪,因?yàn)槿绻?m = 2^p ,則 f(k) 就是 k 的 p 個(gè)最低位數(shù)字。例 12 % 8 = 4 ,12的二進(jìn)制表示位1100,后三位為100。

          • 若散列表長(zhǎng)為 m ,通常 p 為 小于或等于表長(zhǎng)(最好接近m)的最小質(zhì)數(shù)或不包含小于 20 質(zhì)因子的合數(shù)。

          合數(shù):合數(shù)是指在大于1的整數(shù)中除了能被1和本身整除外,還能被其他數(shù)(0除外)整除的數(shù)。

          質(zhì)因子:質(zhì)因子(或質(zhì)因數(shù))在數(shù)論里是指能整除給定正整數(shù)的質(zhì)數(shù)。

          注:這里的2,3,5為質(zhì)因子

          還是上面的例子,我們根據(jù)規(guī)則選擇 5 為 p 值,我們?cè)賮?lái)看。這時(shí)我們發(fā)現(xiàn)只有 6 和 36 沖突,相對(duì)來(lái)說(shuō)就好了很多。

          優(yōu)點(diǎn):計(jì)算效率高,靈活

          應(yīng)用場(chǎng)景:不知道關(guān)鍵字分布情況

          乘法散列法

          構(gòu)造散列函數(shù)的乘法散列法主要包含兩個(gè)步驟

          • 用關(guān)鍵字 k 乘上常數(shù) A(0 < A < 1),并提取 k A 的小數(shù)部分

          • 用 m 乘以這個(gè)值,再向下取整

          散列函數(shù)為

          f (k) = ? m(kA mod 1) ?

          這里的 kA mod 1 的含義是取 keyA 的小數(shù)部分,即 kA - ?kA? 。

          優(yōu)點(diǎn):對(duì) m 的選擇不是特別關(guān)鍵一般選擇它為 2 的某個(gè)冪次(m = 2 ^ p ,p為某個(gè)整數(shù))

          應(yīng)用場(chǎng)景:不知道關(guān)鍵字情況

          平方取中法

          這個(gè)方法就比較簡(jiǎn)單了,假設(shè)關(guān)鍵字是 321,那么他的平方就是 103041,再抽取中間的 3 位就是 030 或 304 用作散列地址。再比如關(guān)鍵字是 1234  那么它的平方就是 1522756 ,抽取中間 3 位就是 227 用作散列地址.

          優(yōu)點(diǎn):靈活,適用范圍廣泛

          適用場(chǎng)景:不知道關(guān)鍵字分布,而位數(shù)又不是很大的情況。

          隨機(jī)數(shù)法

          故名思意,取關(guān)鍵字的隨機(jī)函數(shù)值為它的散列地址。也就是 f(key) = random(key)。這里的random是隨機(jī)函數(shù)。(具體解析見(jiàn)隨機(jī)探測(cè)法)

          適用場(chǎng)景:關(guān)鍵字的長(zhǎng)度不等時(shí)

          上面我們的例子都是通過(guò)數(shù)字進(jìn)行舉例,那么如果是字符串可不可以作為鍵呢?當(dāng)然也是可以的,各種各樣的符號(hào)我們都可以轉(zhuǎn)換成某種數(shù)字來(lái)對(duì)待,比如我們經(jīng)常接觸的ASCII 碼,所以是同樣適用的。

          以上就是常用的散列函數(shù)構(gòu)造方法,其實(shí)他們的中心思想是一致的,將關(guān)鍵字經(jīng)過(guò)加工處理之后變成另外一個(gè)數(shù)字,而這個(gè)數(shù)字就是我們的存儲(chǔ)位置,是不是有一種間諜傳遞情報(bào)的感覺(jué)。

          一個(gè)好的哈希函數(shù)可以幫助我們盡可能少的產(chǎn)生沖突,但是也不能完全避免產(chǎn)生沖突,那么遇到?jīng)_突時(shí)應(yīng)該怎么做呢?下面給大家?guī)?lái)幾種常用的處理散列沖突的方法。

          處理散列沖突的方法

          我們?cè)谑褂?hash 函數(shù)之后發(fā)現(xiàn)關(guān)鍵字 key1 不等于 key2 ,但是 f(key1) = f(key2),即有沖突,那么該怎么辦呢?不急我們慢慢往下看。

          開(kāi)放地址法

          了解開(kāi)放地址法之前我們先設(shè)想以下場(chǎng)景。

          袁記菜館內(nèi),鈴鈴鈴,鈴鈴鈴 電話鈴響了

          大鵬:老袁,給我訂個(gè)包間,我今天要去帶幾個(gè)客戶(hù)去你那談生意。

          袁廚:大鵬啊,你常用的那個(gè)包間被人訂走啦。

          大鵬:老袁你這不仗義呀,咋沒(méi)給我留住呀,那你給我找個(gè)空房間吧。

          袁廚:好滴老哥

          哦,穿越回古代就沒(méi)有電話啦,那看來(lái)穿越的時(shí)候得帶著幾個(gè)手機(jī)了。

          上面的場(chǎng)景其實(shí)就是一種處理沖突的方法-----開(kāi)放地址法

          開(kāi)放地址法就是一旦發(fā)生沖突,就去尋找下一個(gè)空的散列地址,只要列表足夠大,空的散列地址總能找到,并將記錄存入,為了使用開(kāi)放尋址法插入一個(gè)元素,需要連續(xù)地檢查散列表,或稱(chēng)為探查,我們常用的有線性探測(cè),二次探測(cè),隨機(jī)探測(cè)。

          線性探測(cè)法

          下面我們先來(lái)看一下線性探測(cè),公式:

          我們來(lái)看一個(gè)例子,我們的關(guān)鍵字集合為{12,67,56,16,25,37,22,29,15,47,48,21},表長(zhǎng)為12,我們?cè)儆蒙⒘泻瘮?shù) f(key) =  key mod 12。

          我們求出每個(gè) key 的 f(key)見(jiàn)下表

          我們查看上表發(fā)現(xiàn),前五位的 f(key) 都不相同,即沒(méi)有沖突,可以直接存入,但是到了第六位 f(37) = f(25) = 1,那我們就需要利用上面的公式 f(37)  = f (f(37) + 1 ) mod 12 = 2,這其實(shí)就是我們的訂包間的做法。下面我們看一下將上面的所有數(shù)存入哈希表是什么情況吧。

          注:藍(lán)色為計(jì)算哈希值,紅色為存入哈希表

          我們把這種解決沖突的開(kāi)放地址法稱(chēng)為線性探測(cè)法。下面我們通過(guò)視頻來(lái)模擬一下線性探測(cè)法的存儲(chǔ)過(guò)程。


          另外我們?cè)诮鉀Q沖突的時(shí)候,會(huì)遇到 48 和 37 雖然不是同義詞,卻爭(zhēng)奪一個(gè)地址的情況,我們稱(chēng)其為堆積。因?yàn)槎逊e使得我們需要不斷的處理沖突,插入和查找效率都會(huì)大大降低。

          通過(guò)上面的視頻我們應(yīng)該了解了線性探測(cè)的執(zhí)行過(guò)程了,那么我們考慮一下這種情況,若是我們的最后一位不為21,為 34 時(shí)會(huì)有什么事情發(fā)生呢?


          此時(shí)他第一次會(huì)落在下標(biāo)為 10 的位置,那么如果繼續(xù)使用線性探測(cè)的話,則需要通過(guò)不斷取余后得到結(jié)果,數(shù)據(jù)量小還好,要是很大的話那也太慢了吧,但是明明他的前面就有一個(gè)空房間呀,如果向前移動(dòng)只需移動(dòng)一次即可。不要著急,前輩們已經(jīng)幫我們想好了解決方法

          二次探測(cè)法

          其實(shí)理解了我們的上個(gè)例子之后,這個(gè)一下就能整明白了,根本不用費(fèi)腦子,這個(gè)方法就是更改了一下di的取值

          注:這里的是 -1^2  為負(fù)值 而不是 (-1)^2

          所以對(duì)于我們的34來(lái)說(shuō),當(dāng)di = -1時(shí),就可以找到空位置了。

          二次探測(cè)法的目的就是為了不讓關(guān)鍵字聚集在某一塊區(qū)域。另外還有一種有趣的方法,位移量采用隨機(jī)函數(shù)計(jì)算得到,接著往下看吧.

          隨機(jī)探測(cè)法

          大家看到這是不又有新問(wèn)題了,剛才我們?cè)谏⒘泻瘮?shù)構(gòu)造規(guī)則的第一條中說(shuō)

          (1)必須是一致的,假設(shè)你輸入辣子雞丁時(shí)得到的是在看,那么每次輸入辣子雞丁時(shí),得到的也必須為在看。如果不是這樣,散列表將毫無(wú)用處。

          咦?怎么又是在看哈哈,那么問(wèn)題來(lái)了,我們使用隨機(jī)數(shù)作為他的偏移量,那么我們查找的時(shí)候豈不是查不到了?因?yàn)槲覀?di 是隨機(jī)生成的呀,這里的隨機(jī)其實(shí)是偽隨機(jī)數(shù),偽隨機(jī)數(shù)含義為,我們?cè)O(shè)置隨機(jī)種子相同,則不斷調(diào)用隨機(jī)函數(shù)可以生成不會(huì)重復(fù)的數(shù)列,我們?cè)诓檎視r(shí),用同樣的隨機(jī)種子,它每次得到的數(shù)列是相同的,那么相同的 di 就能得到相同的散列地址。

          隨機(jī)種子(Random Seed)是計(jì)算機(jī)專(zhuān)業(yè)術(shù)語(yǔ),一種以隨機(jī)數(shù)作為對(duì)象的以真隨機(jī)數(shù)(種子)為初始條件的隨機(jī)數(shù)。一般計(jì)算機(jī)的隨機(jī)數(shù)都是偽隨機(jī)數(shù),以一個(gè)真隨機(jī)數(shù)(種子)作為初始條件,然后用一定的算法不停迭代產(chǎn)生隨機(jī)數(shù)


          通過(guò)上面的測(cè)試是不是一下就秒懂啦,使用相同的隨機(jī)種子,生成的數(shù)列是相同的。所以為什么我們可以使用隨機(jī)數(shù)作為它的偏移量。

          下面我們?cè)賮?lái)看一下其他的函數(shù)處理散列沖突的方法

          再哈希法

          這個(gè)方法其實(shí)也特別簡(jiǎn)單,利用不同的哈希函數(shù)再求得一個(gè)哈希地址,直到不出現(xiàn)沖突為止。

          f,(key) = RH,( key )    (i = 1,2,3,4…..k)

          這里的RH,就是不同的散列函數(shù),你可以把我們之前說(shuō)過(guò)的那些散列函數(shù)都用上,每當(dāng)發(fā)生沖突時(shí)就換一個(gè)散列函數(shù),相信總有一個(gè)能夠解決沖突的。這種方法能使關(guān)鍵字不產(chǎn)生聚集,但是代價(jià)就是增加了計(jì)算時(shí)間。是不是很簡(jiǎn)單啊。

          鏈地址法

          下面我們?cè)僭O(shè)想以下情景。

          袁記菜館內(nèi),鈴鈴鈴,鈴鈴鈴電話鈴又響了,那個(gè)大鵬又來(lái)訂房間了。

          大鵬:老袁啊,我一會(huì)去你那吃個(gè)飯,還是上回那個(gè)包間

          袁廚:大鵬你下回能不能早點(diǎn)說(shuō)啊,又沒(méi)人訂走了,這回是老王訂的

          大鵬:老王這個(gè)老東西啊,反正也是熟人,你再給我整個(gè)桌子,我拼在他后面吧

          不好意思啊各位同學(xué),信鴿最近太貴了還沒(méi)來(lái)得及買(mǎi)。上面的情景就是模擬我們的新的處理沖突的方法鏈地址法。

          上面我們都是遇到?jīng)_突之后,就換地方。那么我們有沒(méi)有不換地方的辦法呢?那就是我們現(xiàn)在說(shuō)的鏈地址法。

          還記得我們說(shuō)過(guò)的同義詞嗎?就是 key 不同 f(key) 相同的情況,我們將這些同義詞存儲(chǔ)在一個(gè)單鏈表中,這種表叫做同義詞子表,散列表中只存儲(chǔ)同義詞子表的頭指針。我們還是用剛才的例子,關(guān)鍵字集合為{12,67,56,16,25,37,22,29,15,47,48,21},表長(zhǎng)為12,我們?cè)儆蒙⒘泻瘮?shù) f(key) =  key mod 12。我們用了鏈地址法之后就再也不存在沖突了,無(wú)論有多少?zèng)_突,我們只需在同義詞子表中添加結(jié)點(diǎn)即可。下面我們看下鏈地址法的存儲(chǔ)情況。

          鏈地址法雖然能夠不產(chǎn)生沖突,但是也帶來(lái)了查找時(shí)需要遍歷單鏈表的性能消耗,有得必有失嘛。

          公共溢出區(qū)法

          下面我們?cè)賮?lái)看一種新的方法,這回大鵬又要來(lái)吃飯了。

          袁記菜館內(nèi)…..

          袁廚:呦,這是什么風(fēng)把你給刮來(lái)了,咋沒(méi)開(kāi)你的大奔啊。

          大鵬:哎呀媽呀,別那么多廢話了,我快餓死了,你快給我找個(gè)位置,我要吃點(diǎn)飯。

          袁廚:你來(lái)的,太不巧了,咱們的店已經(jīng)滿了,你先去旁邊的小屋看會(huì)電視,等有空了我再叫你。小屋里面還有幾個(gè)和你一樣來(lái)晚的,你們一起看吧。

          大鵬:電視?看電視?

          上面的情景就是模擬我們的公共溢出區(qū)法,這也是很好理解的,你不是沖突嗎?那沖突的各位我先給你安排個(gè)地方呆著,這樣你就有地方住了。我們?yōu)樗袥_突的關(guān)鍵字建立了一個(gè)公共的溢出區(qū)來(lái)存放。

          那么我們?cè)趺催M(jìn)行查找呢?我們首先通過(guò)散列函數(shù)計(jì)算出散列地址后,先于基本表對(duì)比,如果不相等再到溢出表去順序查找。這種解決沖突的方法,對(duì)于沖突很少的情況性能還是非常高的。

          散列表查找算法(線性探測(cè)法)

          下面我們來(lái)看一下散列表查找算法的實(shí)現(xiàn)

          首先需要定義散列列表的結(jié)構(gòu)以及一些相關(guān)常數(shù),其中elem代表散列表數(shù)據(jù)存儲(chǔ)數(shù)組,count代表的是當(dāng)前插入元素個(gè)數(shù),size代表哈希表容量,NULLKEY散列表初始值,然后我們?nèi)绻檎页晒头祷厮饕绻淮嬖谠撛鼐头祷卦夭淮嬖凇?/span>

          我們將哈希表初始化,為數(shù)組元素賦初值。



          插入操作的具體步驟:

          (1)通過(guò)哈希函數(shù)(除法散列法),將key轉(zhuǎn)化為數(shù)組下標(biāo)

          (2)如果該下標(biāo)中沒(méi)有元素,則插入,否則說(shuō)明有沖突,則利用線性探測(cè)法處理沖突。詳細(xì)步驟見(jiàn)注釋


          查找操作的具體步驟:

          (1)通過(guò)哈希函數(shù)(同插入時(shí)一樣),將key轉(zhuǎn)化成數(shù)組下標(biāo)

          (2)通過(guò)數(shù)組下標(biāo)找到key值,如果key一致,則查找成功,否則利用線性探測(cè)法繼續(xù)查找。


          下面我們來(lái)看一下完整代碼

          ??????????????

          散列表性能分析

          如果沒(méi)有沖突的話,散列查找是我們查找中效率最高的,時(shí)間復(fù)雜度為O(1),但是沒(méi)有沖突的情況是一種理想情況,那么散列查找的平均查找長(zhǎng)度取決于哪些方面呢?

          1.散列函數(shù)是否均勻

          我們?cè)谏衔恼f(shuō)到,可以通過(guò)設(shè)計(jì)散列函數(shù)減少?zèng)_突,但是由于不同的散列函數(shù)對(duì)一組關(guān)鍵字產(chǎn)生沖突可能性是相同的,因此我們可以不考慮它對(duì)平均查找長(zhǎng)度的影響。

          2.處理沖突的方法

          相同關(guān)鍵字,相同散列函數(shù),不同處理沖突方式,會(huì)使平均查找長(zhǎng)度不同,比如我們線性探測(cè)有時(shí)會(huì)堆積,則不如二次探測(cè)法好,因?yàn)殒湹刂贩ㄌ幚頉_突時(shí)不會(huì)產(chǎn)生任何堆積,因而具有最佳的平均查找性能

          3.散列表的裝填因子

          本來(lái)想在上文中提到裝填因子的,但是后來(lái)發(fā)現(xiàn)即使沒(méi)有說(shuō)明也不影響我們對(duì)哈希表的理解,下面我們來(lái)看一下裝填因子的總結(jié)

          裝填因子 α  =  填入表中的記錄數(shù)  /  散列表長(zhǎng)度

          散列因子則代表著散列表的裝滿程度,表中記錄越多,α就越大,產(chǎn)生沖突的概率就越大。我們上面提到的例子中 表的長(zhǎng)度為12,填入記錄數(shù)為6,那么此時(shí)的 α = 6  / 12 = 0.5  所以說(shuō)當(dāng)我們的 α 比較大時(shí)再填入元素那么產(chǎn)生沖突的可能性就非常大了。所以說(shuō)散列表的平均查找長(zhǎng)度取決于裝填因子,而不是取決于記錄數(shù)。所以說(shuō)我們需要做的就是選擇一個(gè)合適的裝填因子以便將平均查找長(zhǎng)度限定在一個(gè)范圍之內(nèi)。

          到這里咱們的哈希表總結(jié)就結(jié)束了,因?yàn)槲覀兠魈炀烷_(kāi)始哈希表模塊的面試題總結(jié),所以就寫(xiě)了一篇特別長(zhǎng)的文章來(lái)對(duì)哈希表進(jìn)行總結(jié),希望能對(duì)初學(xué)數(shù)據(jù)結(jié)構(gòu)的同學(xué)帶來(lái)一點(diǎn)點(diǎn)幫助。

          大家快來(lái)打卡哈希表呀!

          參考圖書(shū):

          《小灰的算法之旅》、《數(shù)據(jù)結(jié)構(gòu)與算法分析》、《算法導(dǎo)論》、《大話數(shù)據(jù)結(jié)構(gòu)》、《數(shù)據(jù)結(jié)構(gòu)與算法》


          推薦閱讀



          學(xué)習(xí)更多技能

          請(qǐng)點(diǎn)擊下方公眾號(hào)

          瀏覽 13
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  午夜黄色成人网免费看 | 操逼豆花视频 | 五月天黄色网 | 六月婷婷综合激情无码 | 青娱乐国产精品天堂视频 |