1.什么是哈希表(Hash Tables)
哈希表可以以極快的速度來查找、添加或刪除元素(只需要數次的比較操作。)它比紅黑樹、二叉搜索樹都要快得多。但是哈希表沒有排序功能,類似的,如尋找最大值、最小值、中值這些行為都不能在哈希表中實現。
2.實現哈希表的前提條件
要想對一組元素做成哈希表形式的數據結構,這些元素需要滿足兩個條件:
A. 元素擁有自己的哈希值。
B. 兩個元素可以判斷出是否相等。
第二個條件比較容易實現,關鍵是第一個條件。在介紹哈希表前,將先介紹哈希值。
3. 什么是哈希值(hash code)
哈希值是一個int類型的整數。每個元素都應該有自己的哈希值,並且這個值是唯一的。即滿足:
A. 如果元素a與元素b相等,則元素a的哈希值與元素b的哈希值相等。
B. 如果元素a與元素b不相等,則元素a的哈希值與元素b的哈希值不相等。
通常情況下,對於int,bool,double,string等語言自帶的類型都有自己的哈希值,可以用它們的哈希函數來獲取,不同語言的哈希函數可能會不同。
如果是用戶自己新建的類型,則需要提供計算此類型元素哈希值的哈希函數。
在哈希表中,我們是通過某元素的哈希值來查找、添加或刪除元素的。
4. 如何實現哈希表
有許多方法可以實現哈希表,這里將介紹兩種:拉鏈法(separate chaining)和線性探測法(linear probing)。
5. 拉鏈法(separate chaining)
從例子入手:
現有整數類型(int)的數組S:7,30,10,9,14,19,15,12,90,94,93,53,70。此數組的元素的哈希值與元素值相同,即7的哈希值是7;30的哈希值是30。
現創建一個有5個元素的數組A:(紅色顏色只是為了與數組S的元素區分開來)

然后按順序把數組S插入數組A中:
插入元素7:元素7的哈希值為7,7%5=2,因此插入到2號元素中;
插入元素30:元素30的哈希值為30,30%5=0,因此插入到0號元素中;

插入元素10:元素10的哈希值為10,10%5=0,因此插入到0號元素中,但0號元素已經有數值30了,故插到30后面,元素30的指針指向元素10。

如此類推,插入元素9:元素9的哈希值為9,9%5=4,因此插入到4號元素中...
全部元素添加完畢后:

現在,如果我們要查找元素19:
元素19的哈希值為19,19%5=4,因此到4號元素去找;
4號元素的值為9,9!=19,去找9的指針指向的元素14;
14!=19,去找14的指針指向的元素19;
19==19,返回數值,查找完畢。
此過程只經歷了3次比較!
從此例子中可以看出思路:
對於一個擁有N項元素的數組S,我們需要建立一個含有M項元素的新數組A。
M的值可以自己來定,但如果M過大,則會出現很多空鏈,如上述例子中的1號鏈;
如果M過小,則會出現鏈太長的情況,如果鏈太長,則意味着比較次數變多。
一般建議M=N/5。
插入元素:
int j=S[i]的哈希值%5;
然后檢查A[j]是否為空,如果空,A[j]=S[i]; 如果不為空,temp=A[j], A[j]=S[i], S[i].next=temp;
刪除元素:先找出該元素,然后此元素的上一個元素的指針指向此元素的下一個元素,最后刪除此元素。
拉鏈法有一個缺陷,就是很容易有些鏈過長,有些鏈過短,甚至是空鏈。這樣會浪費內存和影響搜索速度。
6. 線性探測法(linear probing)
從例子入手:
現有整數類型(int)的數組S:7,30,10,9,14,19,15,29,13,27。此數組的元素的哈希值與元素值相同,即7的哈希值是7;30的哈希值是30。
現創建一個含有14個元素的數組A:(上面那排數字是為了方便看哪個元素是第幾項元素)

然后按順序把數組S插入數組A中:
插入元素7:元素7的哈希值為7,7%14=7,因此插入到7號元素中;
插入元素30:元素30的哈希值為30,30%14=2,因此插入到2號元素中;
插入元素10:元素10的哈希值為10,10%14=10,因此插入到10號元素中;
插入元素9:元素9的哈希值為9,9%14=9,因此插入到9號元素中;
插入元素14:元素14的哈希值為14,14%14=0,因此插入到0號元素中;
插入元素19:元素19的哈希值為19,19%14=5,因此插入到5號元素中;
插入元素15:元素15的哈希值為15,15%14=1,因此插入到1號元素中;

插入元素29:元素29的哈希值為29,29%14=1,但1號元素已經有數值15了,然后檢查下一個元素(2號元素),但2號元素已經有數值30了,然后檢查下一個元素(3號元素),3號元素為空,插入到3號元素:

插入元素13:元素13的哈希值為13,13%14=13,因此插入到13號元素中:

插入元素27:元素27的哈希值為27,27%14=13,但13號元素已經有數值13了,然后檢查下一個元素(0號元素),但0號元素已經有數值14了,如此類推,找到4號元素為空,插入到4號元素:

如果我們要查找元素29,元素29的哈希值為29,29%14=1,檢查1號元素,15!=29;然后檢查下一個元素(2號元素),30!=29;然后檢查下一個元素(3號元素),29=29,返回數值,查找完畢。
從此例子中可以看出思路:
對於一個擁有N項元素的數組S,我們需要建立一個含有M項元素的新數組A。
M一定要比N大。如果M過少,則搜索進行的比較次數增加,影響速度;如果過大,則太多空位沒利用,造成內存浪費。
一般建議M=2*N。
插入元素i: 先獲取i的哈希值%M,根據這個值插入到相應的位置中,如果位置有元素了,則插入到下一個位置,循環進行,直到位置是空的為止。
搜索元素:類似於插入元素,詳細見上述例子。
