散列算法和哈希表結構
算法概述
- Hash ,一般翻譯做“ 散列” ,也有直接音譯為“ 哈希” 的,就是把任意長度的輸入(又叫做預映射, pre-image ),通過散列算法,變換成固定長度的輸出,該輸出就是散列值。這種轉換是一種壓縮映射,也就是,散列值的空間通常遠小於輸入的空間,不 同的輸入可能會散列成相同的輸出,而不可能從散列值來唯一的確定輸入值。簡單的說就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的函數。
哈希表
- 數組的特點是:尋址容易,插入和刪除困難;而鏈表的特點是:尋址困難,插入和刪除容易。那么我們能不能綜合兩者的特性,做出一種尋址容易,插入刪除也容易 的數據結構?答案是肯定的,這就是我們要提起的哈希表
- 哈希表是一種典型的“空間換時間”的做法
- 哈希表有多種不同的實現方法,我接下來解釋的是最常用的一種方法—— 拉鏈法
- HashMap其實也是一個線性的數組實現的,所以可以理解為其存儲數據的容器就是一個線性數組,如圖所示,從0連續到15。這可能讓我們很不解,一個線性的數組怎么實現按鍵值對來存取數據呢?這里HashMap有做一些處理。
- 首先HashMap里面實現一個靜態內部類Entry 其重要的屬性有 key , value, next,從屬性key,value我們就能很明顯的看出來Entry就是HashMap鍵值對實現的一個基礎bean,我們上面說到HashMap的基 礎就是一個線性數組,這個數組就是Entry[],Map里面的內容都保存在Entry[]里面。
- 既然是線性數組,為什么能隨機存取?這里HashMap用了一個小算法,大致是這樣實現:
存儲時:
int hash = key.hashCode();--> 這個hashCode方法這里不詳述,只要理解每個key的hash是一個固定的int值
int index = hash % Entry[].length;
Entry[index] = value;
取值時:
int hash = key.hashCode();
int index = hash % Entry[].length;
return Entry[index]
hash沖突
- 如果兩個key通過hash % Entry[].length得到的index相同,會不會有覆蓋的危險?
這里HashMap里面用到鏈式數據結構的一個概念.上面我們提到過Entry類里面有一個next屬性,作用是指向下一個Entry。打個比方, 第一個鍵值對A進來,通過計算其key的hash得到的index=0,記做:Entry[0] = A.一會后又進來一個鍵值對B,通過計算其index也等於0,現在怎么辦?HashMap會這樣做:B.next = A,Entry[0] = B,如果又進來C,index也等於0,那么C.next = B,Entry[0] = C;這樣我們發現index=0的地方其實存取了A,B,C三個鍵值對,他們通過next這個屬性鏈接在一起。所以疑問不用擔心。 - 解決方法
- 鏈地址法
- 種方法的基本思想是將所有哈希地址為i的元素構成一個稱為同義詞鏈的單鏈表,並將單鏈表的頭指針存在哈希表的第i個單元中,因而查找、插入和刪除主要在同義詞鏈中進行。鏈地址法適用於經常進行插入和刪除的情況。
- 鏈地址法
負載因子
- 比如我們存儲70個元素,但我們可能為這70個元素申請了100個元素的空間。70/100=0.7,這個數字稱為負載因子。
常見hash算法
- MD5
- SHA-1 及其他
Hash構造函數的方法
-
直接尋址法:取keyword或keyword的某個線性函數值為散列地址。即H(key)=key或H(key) = a•key + b,當中a和b為常數(這樣的散列函數叫做自身函數)
-
.數字分析法:分析一組數據,比方一組員工的出生年月日,這時我們發現出生年月日的前幾位數字大體同樣,這種話,出現沖突的幾率就會非常大,可是我們發現年月日的后幾位表示月份和詳細日期的數字區別非常大,假設用后面的數字來構成散列地址,則沖突的幾率會明顯減少。因此數字分析法就是找出數字的規律,盡可能利用這些數據來構造沖突幾率較低的散列地址。
-
平方取中法:取keyword平方后的中間幾位作為散列地址。
-
.折疊法:將keyword切割成位數同樣的幾部分,最后一部分位數能夠不同,然后取這幾部分的疊加和(去除進位)作為散列地址。
-
隨機數法:選擇一隨機函數,取keyword的隨機值作為散列地址,通經常使用於keyword長度不同的場合。
-
除留余數法:取keyword被某個不大於散列表表長m的數p除后所得的余數為散列地址。即 H(key) = key MOD p, p<=m。不僅能夠對keyword直接取模,也可在折疊、平方取中等運算之后取模。對p的選擇非常重要,一般取素數或m,若p選的不好,easy產生同義詞。
性能比較
- 一般來說,使用 散列表 會比 紅黑樹 快很多。但具體還是要看 哈希函數 的計算效率。
- 但是 散列表 無法保證順序,所以如果你需要進行有關順序的操作,應該使用 紅黑樹 或者 二叉搜索樹 。