1. 程式人生 > 實用技巧 >雜湊表(hash)

雜湊表(hash)

散列表Hash table,也叫雜湊表),是根據(Key)而直接訪問在記憶體儲存位置的資料結構。也就是說,它通過計算一個關於鍵值的函式,將所需查詢的資料對映到表中一個位置來訪問記錄,這加快了查詢速度。這個對映函式稱做雜湊函式,存放記錄的陣列稱做散列表(來自維基百科)

其中前邊說到的離散化也是一種特殊的雜湊方式,只不過離散化注重保序性,因此使用二分查詢的方法。

其中存在問題就是:可能會把不同的數對映成相同的數,這就是雜湊衝突,則我們處理衝突的方法就是將一組關鍵字對映到一個有限的連續的地址集(區間)上,到時候我們查詢的時候就可以順著這個地址依次查詢。

而處理衝突的兩種方法:拉鍊法和開放定址法

拉鍊法:

這種方法就是把對映值相同的點像連結串列一樣掛在同一個地址上,當我們尋找的時候就可以通過地址來直接索引。

而尋找這個地址或者說對映的方法就是取模(mod),而mod的數最好就是大於對映範圍的第一個質數,這樣會更大的減少衝突(數學推理不清楚,聽大佬說的)。

取模的方法:k = (x % N + N) %N(N是對映後的範圍,這樣取模是為了防止負數)

開放定址法

這種方法只需要開一個數組,不過這個陣列的大小最好是對映後範圍的2~3倍,那是因為這種方法再尋找對映後結果如果被佔用則它順著這個結果繼續向下找直到找到空位。

說的形象一點就好比上廁所:這個坑位有人,咱就必須取下一個坑位,直到找到一個空的坑位。

以一個題目作為例子

模擬散列表

維護一個集合,支援如下幾種操作:
“Ix”,插入一個數x; “Qx”,詢問數x是否在集合中出現過; 現在要進行N次操作,對於每個詢問操作輸出對應的結果。
輸入格式 第一行包含整數N,表示運算元量。
接下來N行,每行包含一個操作指令,操作指令為”Ix”,”Qx”中的一種。
輸出格式 對於每個詢問指令“Qx”,輸出一個詢問結果,如果x在集合中出現過,則輸出“Yes”,否則輸出“No”。
每個結果佔一行。
資料範圍 1≤N≤10^5 −10^9≤x≤10^9 輸入樣例: 5 I1 I2 I3 Q2 Q5 輸出樣例: Yes No 拉鍊法Code:
 1 #include <iostream>
 2 #include <cstring>
 3 
 4 using namespace std;
 5 
 6 const int N = 100003; //尋找一個大於對映範圍的第一個質數 最好用質數取模
 7 int e[N], ne[N], idx, h[N];
 8 
 9 void insert(int x)
10 {   
11     int k = (x % N + N) % N;
12     e[idx] = x;
13     ne[idx] = h[k];
14     h[k] = idx;
15     idx++;
16 }
17 
18 bool query(int x)
19 {
20     int k = (x % N + N) %N;
21     for(int i = h[k]; i != -1; i = ne[i])
22     {
23         if(e[i] == x)
24         {
25             return true;
26         }
27     }
28 
29     return false;
30 }
31 
32 int main()
33 {
34     int n;
35     scanf("%d", &n);
36 
37     memset(h, -1, sizeof(h));
38 
39     while(n--)
40     {
41         int x;
42         char op[2];
43         scanf("%s%d", op, &x);
44         if(*op == 'I') insert(x);
45         else
46         {
47             if(query(x)) printf("Yes\n");
48             else printf("No\n");
49         }
50     }
51 
52     system("pause");
53     return 0;
54 }

開放定址法Code:

 1 #include <iostream>
 2 #include <cstring>
 3 
 4 using namespace std;
 5 
 6 const int N = 200003, null = 0x3f3f3f3f; //陣列開到個數上限的2~3倍, null表示為空 未被佔用
 7 int h[N];
 8 
 9 int finds(int x) //兩個作用:1.尋找可以插入的位置 2.尋找雜湊表中是否存在要查詢的數字
10 {
11     int t = (x % N + N) % N;
12     while(h[t] != null && h[t] != x)
13     {
14         t++;
15         if(t == N) t = 0; //如果找到尾則從頭尋找
16     }
17 
18     return t;
19 }
20 
21 int main()
22 {
23     memset(h, 0x3f, sizeof(h));  //尋找一個標誌 這個標誌大於x的範圍
24 
25     int n;
26     scanf("%d", &n);
27 
28     while(n--)
29     {
30         char op[2];
31         int x;
32 
33         scanf("%s%d", op, &x);
34         if(*op == 'I') h[finds(x)] = x;
35         else
36         {
37             if(h[finds(x)] != null) puts("Yes");
38             else puts("No");
39         }
40     }
41 
42     system("pause");
43     return 0;
44 }

字串雜湊

字串雜湊就是把一個字串雜湊為整數,具體方法就是把一個字串具體看成一個P進位制數(P不確定),然後我們把他換算成十進位制數字,這樣就可以直接通過數字來判斷兩個字串是否相等。(相當厲害並且好用的一種方法)。