散列表
- 定義
散列表是根據(key value)來直接進行訪問的數據結構。它通過把關鍵碼應射鵰一個數組中的一個位置來訪問記錄,以加快查找的速度。這個映射的函數叫散列函數。存放記錄的數組叫做散列表
- 散列能做什麼
例如word的單詞拼寫檢查功能,假設有一百萬個單詞,每個單詞平局長度爲10個字母,也就是佔用十字節的空間。100萬個單詞四捨五入也就是佔用10M的空間。我們將其映射到散列表中。當用戶輸入時就去散列表中查找。如果查不到,則說明拼寫錯誤予以提示。
- 散列函數
散列函數,顧名思義他是一個函數。我們可以把它定義成hash(key),其中key表示元素的鍵值,hash(key)的值表示經過散列函數計算得到的散列值。
- 構造散列函數的要求
- 散列函數計算得到的散列值是一個非負整數
- 如果key1 = key2,那hash(key1) == hash(key2)
- 如果key1 != key2, 那hash(key1) != hash(key2)
- 儘量的簡單,速度快
- 散列表長度儘量爲素數。這樣可以使映射分配均勻
假設有兩個散列表,a表長度爲7,b表長度爲8,步長爲2的產生數據。假設數據只到1000
那麼a表分佈爲
b表分佈爲
可以非常明顯地看到,a表均勻的分佈。但是b表堆積明顯
- 散列衝突
如果key1 != key2, 那hash(key1) == hash(key2),我們稱之爲散列衝突。想要避免散列衝突幾乎不可能。即使MD5、SHA、CRC等哈希算法,也無法完全避免散列衝突。
- 散列衝突的解決
- 分離鏈接法
簡單來說就是再原有的散列表後加上鍊表來存放散列衝突的元素。
優點:
可以解決任意次衝突
刪除操作簡單、統一
缺點:
耗費內存
空間並不連續,緩存幾乎失效
- 線性探測法
當散列表插入數據遇到衝突時,將其放入下一個空閒地址。
線性探測的效率視裝填因子大小而定
(裝填因子 = 填入表中元素個數 / 散列表的長度)
- 平方探測法
當散列衝突時查找1^2遠(+-1)處是否爲空,如果還衝突則找2^2遠處以此類推。
定理:如果使用平方探測,且表的大小爲素數,那麼當表至少有一半是空的時候,總能夠插入一個新的元素
- 雙散列
雙散列可以看做F(i)= i * hash2( x ) 其基本策略和線性探測法一項,唯一不同是:它不是檢查衝突位置後的每一個位置,而是採用另一個散列函數產生一個固定的增量。
注:第二個散列函數要仔細選擇,需滿足條件
(1)排除散列值是0的情況
(2)產生的散列值必須與表長M互素
七、散列優化
1、再散列
當裝載因子過大時,操作時間將開始過長,插入操作可能失敗。解決辦法是,新建個大約長度爲散列表兩倍的新表,掃描整個原始散列,計算所有元素新的散列值插入新的散列表中。
- 鏈接法缺陷解決
如果分離鏈接法鏈表過長,會嚴重影響性能,鏈表就要轉爲紅黑樹(一般再長度爲8時轉成紅黑樹),利用紅黑樹快速增刪改查的特點,
提高性能