算法思想:ios
哈希表算法
什麼是哈希表數組
在前面討論的各類結構(線性表、樹等)中,記錄在結構中的相對位置是隨機的,和記錄的關鍵字之間不存在肯定的關係,所以,在結構中查找記錄時需進行一系列和關鍵字的比較。這一類查找方法創建在「比較」的基礎上。dom
在順序查找時,比較的結果爲「="與「!=」兩種可能;函數
在折半查找、二叉排序樹查找和B樹查找時,比較的結果爲「<"、"="和「>"3種可能。查找的效率依賴於查找過程當中所進行的比較次數。spa
理想的狀況是但願不通過任何比較,一次存取便能獲得所查記錄,那就必須在記錄的存儲位置和它的關鍵字之間創建一個肯定的對應關係f,使每一個關鍵字和結構中一個唯一的存儲位置相對應。於是在查找時,只要根據這個對應關係f找到給定值K的像f(K)。若結構中存在關鍵字和K相等的記錄,則一定在f(K)的存儲位置上,由此,不須要進行比較即可直接取得所查記錄。在此,咱們稱這個對應關係f爲哈希( Hash)函數,按這個思想創建的表爲哈希表。3d
哈希函數的構造方法指針
哈希函數是從關鍵字集合到地址集合的映像。一般,關鍵字集合比較大,它的元素包括全部可能的關鍵字,而地址集合的元素僅爲哈希表中的地址值。哈希函數實際上是一個壓縮映像,那麼這種狀況就不可避免的產生衝突,那麼在建造哈希表時不只要設定一個好的哈希函數,還要設定一種處理衝突的方法。(設定的哈希函數H(key)和處理衝突的方法將一組關鍵字映像到一個有限的連續的地址集上,並以關鍵字在地址集中的「像」做爲記錄在表中的存儲位置,這種表就是哈希表,映像的過程爲哈希造表或散列,所得的存儲位置稱哈希地址或散列地址)code
(1)直接定址法blog
取關鍵字或關鍵字的某個線性函數值爲哈希地址。即H(key)=key 或 H(key)=a*key+b (a,b爲常數)。
舉例1:統計1-100歲的人口,其中年齡做爲關鍵字,哈希函數取關鍵字自身。查找年齡25歲的人口有多少,則直接查表中第25項。
地址 01 02 03 ... 25 26 27 ... 100
年齡 1 2 3 ... 25 26 27 ... ....
人數 3000 2000 ............. 1050
...
舉例2:統計解放之後出生人口,其中年份做爲關鍵字,哈希函數取關鍵字自身加一個常數H(key)=key+(-1948).查找1970年出生的人數,則直接查(1970-1948)=22項便可。
地址 01 02 03 ... 22 23 24 ...
年份 1949 1950 1951 ... 1970
人數 ............. 15000
...
(2)數字分析法
若關鍵字是以r爲基的數(如:以10爲基的十進制數),而且哈希表中可能出現的關鍵字都是事先知道的,則可取關鍵字的若干數位組成哈希地址。
舉例:有80個記錄,其關鍵字爲8位十進制數,假設哈希表長,則可取兩位十進制數組成哈希地址,爲了儘可能避免衝突,可先分析關鍵字。
8 1 3 4 6 5 3 2
8 1 3 7 2 2 4 2
8 1 3 8 7 4 2 2
8 1 3 0 1 3 6 7
8 1 3 2 2 8 1 7
8 1 3 3 8 9 6 7
8 1 3 5 4 1 5 7
8 1 3 6 8 5 3 7
8 1 4 1 9 3 5 5 ...........
經分析,發現第一位、第二位都是8,1,第三位只可能取3或4,第八位只可能取2,5或7,因此這四位不可取,那麼對於第4、5、6、七位可當作是隨機的,所以,可取其中任意兩位,或取其中兩位與另外兩位的疊加求和捨去進位做爲哈希地址。
(3)平方取中法
取關鍵字平方後的中間幾位爲哈希地址。(較經常使用的一種)
舉例:爲BASIC源程序中的標識符鍵一個哈希表(假設BASIC語言容許的標識符爲一個字母或者一個字母和一個數字兩種狀況,在計算機內可用兩位八進制數表示字母和數字),假設表長爲512=,則可取關鍵字平方後的中間9位二進制數爲哈希地址。(每3個二進制位可表示1位八進制位,即3個八進制位爲9個二進制位)
A :01 (A的ASCII碼值爲65,65的八進制爲101,取後兩位表示關鍵字)
B:02 (B的ASCII碼值爲66,66的八進制爲102,取後兩位表示關鍵字)
...
Z:32(Z的ASCII碼值爲90,90的八進制爲132,取後兩位表示關鍵字)
...
0:60(0的ASCII碼值爲48,48的八進制爲60,取後兩位表示關鍵字)
...
9:71(9的ASCII碼值爲57,57的八進制爲71,取後兩位表示關鍵字)
記錄 關鍵字 關鍵字的平方 哈希地址(~)
A 0100 0010000 010
I 1100 1210000 210
P1 2061 4310541 310
Q2 2162 4741304 741
(4)摺疊法
將關鍵字分割成位數相同的幾部分(最後一部分的位數可不一樣),而後取這幾部分的疊加和(捨去進位)做爲哈希地址。適用於關鍵字位數比較多,且關鍵字中每一位上數字分佈大體均勻時。
舉例:根據國際標準圖書編號(ISBN)創建一個哈希表。如一個國際標準圖書編號 0-442-20586-4的哈希地址爲:
5864 5864
4220 0224
+ 04 + 04
10088 6092
移位疊加 間接疊加
H(key)=0088(將分割後的每一部分的最低位對齊) H(key)=6092(從一端向另外一端沿分割界來回疊加)
(5)除留餘數法
取關鍵字被某個不大於哈希表表長m的數p除後所得餘數爲哈希地址(p爲素數)
H(key)=key MOD p,p<=m (最簡單,最經常使用)p的選取很重要
通常狀況,p能夠選取爲質數或者不包含小於20的質因數的合數(合數指天然數中除了能被1和自己整除外,還能被其餘數(0除外)整除的數)。
(6)隨機數法
選擇一個隨機函數,取關鍵字的隨機函數值爲它的哈希地址。即H(key)=random(key),其中random爲隨機函數。適用於關鍵字長度不等時。
總結:實際工做中根據狀況不一樣選用的哈希函數不一樣,一般,考慮因素以下:
(1)計算哈希函數所需時間(包括硬件指令的因素)
(2)關鍵字的長度
(3)哈希表的大小
(4)關鍵字的分佈狀況
(5)記錄的查找頻率
經常使用衝突處理方法:
1.開放定址法:
方法: fi(key)=(f(key)+di) mod m,(di=1,2,3,4...,m−1)fi(key)=(f(key)+di) mod m,(di=1,2,3,4...,m−1)
線性探測:只要一旦發現衝突,就尋找下一個空的散列地址
二次探測:di=12,−12,22,−22,...,q2,−q2di=12,−12,22,−22,...,q2,−q2,目的是不讓關鍵詞集中在某塊區域,產生堆積
隨機探測:didi是一個隨機數,但查詢時須要設置和插入時相同的隨機種子
2.再散列函數法:(再哈希法)
方法:fi(key)=RHi(key) (i=1,2,...k)fi(key)=RHi(key) (i=1,2,...k)
遇到衝突就從新採用一個散列函數計算新的存儲位置,可使關鍵字不產生彙集
3.鏈地址法(拉鍊)
方法:將全部關鍵字的同義詞記錄在一個單鏈表中,在散列表中只存儲全部同義詞表的頭指針
4.創建一個公共溢出區法
方法:爲全部衝突的關鍵字開闢一個公共的溢出區(表)來存放
適用於相對於基本表來講衝突數據不多的狀況
實現方法:(哈希表採用數組存儲,哈希函數構造和處理衝突的方法是除留餘數法+開放定址法)
1 /**** 2 * Hash Table 3 * 4 ****/ 5 6 7 //#include "Global.h" 8 #include"stdafx.h" 9 #include <iostream> 10 using namespace std; 11 12 // HashTable Data Structure Definition 13 // array hashtable 14 #define tablesize 10 15 typedef int HashTable[tablesize]; 16 //hash function initialization way 17 void Initial_HashTable(HashTable &ht) 18 { 19 for (int i = 0; i < tablesize; i++) 20 ht[i] = 0; 21 } 22 //search hashtable function 23 int Search_HashTable(HashTable &ht,int key) 24 { 25 int address = key%tablesize; 26 int compare = 0; 27 while (compare < tablesize&&ht[address] != key&&ht[address] != 0) 28 { 29 compare++; 30 address = (address+1)%tablesize; 31 } 32 if (compare == 10 || ht[address] == 0) 33 cout << "can not find elem" << endl; 34 return address; 35 } 36 //insert hashtable function 37 int Insert_HashTable(HashTable &ht,int key) 38 { 39 int res = Search_HashTable(ht,key); 40 if (ht[res] == 0) 41 { 42 ht[res] = key; 43 return 1; 44 } 45 return 0; 46 } 47 //test function 48 int main() 49 { 50 int data[8] = { 25,36,39,47,20,58,16,35 }; 51 HashTable ht; 52 53 //initialization. 54 Initial_HashTable(ht); 55 56 //insert datas. 57 for (int i = 0; i < 8; i++) 58 { 59 cout << Insert_HashTable(ht, data[i]) << " "; 60 } 61 cout << endl; 62 63 //search. 64 cout << "25 : " << Search_HashTable(ht, 25) << endl; 65 cout << "35 : " << Search_HashTable(ht, 35) << endl; 66 cout << "145 : " << Search_HashTable(ht, 145) << endl; 67 system("pause"); 68 return 0; 69 }