數據結構與算法-散列表

一、散列表的由來?

1.散列表來源於數組,它藉助散列函數對數組這種數據結構進行擴展,利用的是數組支持按照下標隨機訪問元素的特性。

2.需要存儲在散列表中的數據我們稱為鍵,將鍵轉化為數組下標的方法稱為散列函數,散列函數的計算結果稱為散列值。

3.將數據存儲在散列值對應的數組下標位置。

二、如何設計散列函數?

總結3點設計散列函數的基本要求

1.散列函數計算得到的散列值是一個非負整數。

2.若key1=key2,則hash(key1)=hash(key2)

3.若key≠key2,則hash(key1)≠hash(key2)

正是由於第3點要求,所以產生了幾乎無法避免的散列衝突問題。

三、散列衝突的解放方法?

1.常用的散列衝突解決方法有2類:開放尋址法(open addressing)和鏈表法(chaining)

2.開放尋址法

①核心思想:如果出現散列衝突,就重新探測一個空閒位置,將其插入。

②線性探測法(Linear Probing):

插入數據:當我們往散列表中插入數據時,如果某個數據經過散列函數之後,存儲的位置已經被佔用了,我們就從當前位置開始,依次往後查找,看是否有空閒位置,直到找到為止。

查找數據:我們通過散列函數求出要查找元素的鍵值對應的散列值,然後比較數組中下標為散列值的元素和要查找的元素是否相等,若相等,則說明就是我們要查找的元素;否則,就順序往後依次查找。如果遍歷到數組的空閒位置還未找到,就說明要查找的元素並沒有在散列表中。

刪除數據:為了不讓查找算法失效,可以將刪除的元素特殊標記為deleted,當線性探測查找的時候,遇到標記為deleted的空間,並不是停下來,而是繼續往下探測。

結論:最壞時間複雜度為O(n)

③二次探測(Quadratic probing):線性探測每次探測的步長為1,即在數組中一個一個探測,而二次探測的步長變為原來的平方。

④雙重散列(Double hashing):使用一組散列函數,直到找到空閒位置為止。

⑤線性探測法的性能描述:

用“裝載因子”來表示空位多少,公式:散列表裝載因子=填入表中的個數/散列表的長度。

裝載因子越大,說明空閒位置越少,衝突越多,散列表的性能會下降。

3.鏈表法(更常用)

插入數據:當插入的時候,我們需要通過散列函數計算出對應的散列槽位,將其插入到對應的鏈表中即可,所以插入的時間複雜度為O(1)。

查找或刪除數據:當查找、刪除一個元素時,通過散列函數計算對應的槽,然後遍歷鏈表查找或刪除。對於散列比較均勻的散列函數,鏈表的節點個數k=n/m,其中n表示散列表中數據的個數,m表示散列表中槽的個數,所以是時間複雜度為O(k)。

四、思考

1.Word文檔中單詞拼寫檢查功能是如何實現的?

字符串佔用內存大小為8字節,20萬單詞佔用內存大小不超過20MB,所以用散列表存儲20萬英文詞典單詞,然後對每個編輯進文檔的單詞進行查找,若未找到,則提示拼寫錯誤。

2.假設我們有10萬條URL訪問日誌,如何按照訪問次數給URL排序?

字符串佔用內存大小為8字節,10萬條URL訪問日誌佔用內存不超過10MB,通過散列表統計url訪問次數,然後用TreeMap存儲散列表的元素值(作為key)和數組下標值(作為value)

3.有兩個字符串數組,每個數組大約有10萬條字符串,如何快速找出兩個數組中相同的字符串?

分別將2個數組的字符串通過散列函數映射到散列表,散列表中的元素值為次數。注意,先存儲的數組中的相同元素值不進行次數累加。最後,統計散列表中元素值大於等於2的散列值對應的字符串就是兩個數組中相同的字符串


分享到:


相關文章: