1. 程式人生 > >雜湊表及其常用演算法(程式碼實現)

雜湊表及其常用演算法(程式碼實現)

轉載自—>http://blog.csdn.net/wangxu_zju_2010/article/details/7489548

整理了一下Hash表相關內容,如下:

Hash 表是使用 O(1) 時間進行資料的插入刪除和查詢,但是 hash 表不保證表中資料的有序性,這樣在 hash 表中查詢最大資料或者最小資料的時間是 O(N) 。

1 定址和 hash 函式

         理想狀態下 hash 足夠大,每一資料儲存在一個 hash 儲存單元內,這樣對於插入刪除和查詢某一個數據就可以直接得到。但是現實情況下 hash 表不可能無限大,而且理論上儲存的資料的個數是沒有限制的,這樣儲存的資料的數量就遠遠大於 hash 表的儲存單元的數量。

         為了實現在 O(1) 內對資料進行插入刪除和查詢,就必須將一個數據對映到 hash 表中的固定位置,這個對映函式就是 hash 函式。 Hash 函式通過對資料進行計算得到一個在 hash 表中的位置地址。

 

圖 1.1 理想的 hash 表

         要選擇較好的 hash 函式,以及 hash 表儲存單元的數量,這樣才能使儲存在 hash 表中的資料均勻分佈。

         理想狀態不太可能實現,由於儲存的資料數量遠遠大於 hash 表儲存單元的數量,所以再好的 hash 函式也可能使不同的資料得到相同的對映位置,這就造成了衝突。但是好的 hash 函式可以將這種衝突降到最低。

2 分離連結

         解決這種衝突的第一種方法是藉助連結串列來實現,就是將資料實際存放在與 hash 表儲存單元相連結的連結串列中,而不是 hash 的儲存單元中。

 

圖 2.1 分離連結串列

         當產生衝突的時候,將兩個資料都連結在同一 hash 儲存單元儲存的連結串列中。當一個儲存單元儲存的連結串列中有多個數據的時候,對於連結串列後面的資料的查詢新增和刪除就是不是嚴格意義上的 O(1) 了。一個好的 hash 函式可以使得這個連結串列很短。最壞情況下,當所有的資料都儲存在一個 hash 單元指定的連結串列中的時候,那麼這個 hash 就和連結串列一樣了。

3 開放地址

         使用開放地址方法解決衝突的時候,資料仍然儲存在 hash 表的儲存單元中,但是當衝突發生的時候,要再次計算新的地址。

         常用的開放地址法是線性探查,就是當對一個數據進行插入刪除或者查詢的時候,通過 hash 函式計算,發現這個位置不是要找的資料,這時候就檢查下一個儲存單元,一直找到要操作的資料為止。

         除了線性探查外還有二次探查,再 hash 等等方法,都是當一次計算得到的位置不是要找到的資料的時候,怎樣再次確定新的位置。

4 完全 hash 表

         採用分離連結串列的方式解決衝突的時候,當多個數據被對映到一個地址的時候,它們就形成了一個連結串列,要操作這其中的一個數據,那麼就必須在這個連結串列中進行操作。如果 hash 函式選擇的好的話,連結串列會很短,這樣的操作近似 O(1) ,但並不是精確的等於 O(1) ,它與連結串列的長度有關。對於資料的訪問的最壞情況的訪問也是 O(1) 的 hash 叫做完全 hash 表。

         這樣的 hash 表是一個兩級的 hash 表,第一級的 hash 與使用分離連結方法的 hash 一樣,但是 hash 儲存單元中指向的不是一個連結串列,而是另一個 hash 表。

 

圖 4.1 完全 hash 表

         要小心的選擇一級以及二級的 hash 函式可以完全保證在二級 hash 表中不會出現衝突。

5 常用演算法

  1. 直接定址法 :地址集合 和 關鍵字集合大小相同
  2. 數字分析法 :根據需要hash的 關鍵字的特點選擇合適hash演算法,儘量尋找每個關鍵字的 不同點
  3. 平方取中法:取關鍵字平方之後的中間極為作為雜湊地址,一個數平方之後中間幾位數字與數的每一位都相關,取得位數由表長決定。比如:表長為512,=2^9,可以取平方之後中間9位二進位制數作為雜湊地址。
  4. 摺疊法:關鍵字位數很多,而且關鍵字中每一位上的數字分佈大致均勻的時候,可以採用摺疊法得到雜湊地址,
  5. 除留取餘法除P取餘,可以選P為質數,或者不含有小於20的質因子的合數
  6. 隨機數法:通常關鍵字不等的時候採用此法構造雜湊函式較恰當。

 實際工作中需要視不同的情況採用不同的hash函式:

  1. 考慮因素:計算雜湊函式所需要的時間,硬體指令等因素。
  2. 關鍵字長度
  3. 雜湊表大小
  4. 關鍵字分佈情況
  5. 記錄查詢的頻率。(huffeman樹)
具體程式碼如下: [cpp] view plain copy  print?
  1. <span style="font-size:16px;">#include<stdio.h>  
  2. #include<stdlib.h>
  3. #include<math.h>
  4. struct HashTable;  
  5. struct ListNote;  
  6. typedefstruct HashTable *HashTbl;  
  7. typedefstruct ListNote *Position;  
  8. typedef Position List;  
  9. int Hash(int key,int tablesize);  
  10. int NextPrime(int x);  
  11. HashTbl InitalizeTable(int TableSize);  
  12. void DestroyTable(HashTbl H);  
  13. Position Find(int key,HashTbl H);  
  14. void Insert(int key, HashTbl H);  
  15. void Delete(int key,HashTbl H);  
  16. struct HashTable{   
  17.     int TableSize;  
  18.     Position *TheList;  
  19. };  
  20. struct ListNote{  
  21.     int element;  
  22.     Position next;  
  23. };  
  24. int Hash(int key,int tablesize){  
  25.     return key%tablesize;  
  26. }  
  27. int NextPrime(int x){  
  28.     int flag;  
  29.     while(1){  
  30.         flag = 0;  
  31.         int i;  
  32.         int n = sqrt((float)x);  
  33.         for(i = 2 ;i <= n;i++){  
  34.             if(x % i == 0){  
  35.                 flag = 1;  
  36.                 break;  
  37.             }  
  38.         }  
  39.         if(flag == 0)  
  40.             return x;  
  41.         else
  42.             x++;  
  43.     }  
  44. }  
  45. HashTbl InitalizeTable(int TableSize){  
  46.     if(TableSize <= 0){  
  47.         printf("雜湊大小有問題\n");  
  48.         return NULL;  
  49.     }  
  50.     HashTbl table = (HashTbl)malloc(sizeof(struct HashTable));  
  51.     if(table == NULL)  
  52.         printf("分配失敗");  
  53.     table->TableSize = NextPrime(TableSize);  
  54.     table->TheList = (Position*)malloc(sizeof(List) * table->TableSize);  
  55.     if(table->TheList == NULL)  
  56.         printf("分配失敗");  
  57.     table->TheList[0] = (Position)malloc(table->TableSize*sizeof(struct ListNote));  
  58.     if(table->TheList == NULL)  
  59.         printf("分配失敗");  
  60.     int i;  
  61.     for(i = 0;i < table->TableSize;i++){  
  62.         table->TheList[i] = table->TheList[0] + i;  
  63.         table->TheList[i]->next = NULL;  
  64.     }  
  65.     return table;  
  66. }  
  67. Position Find(int key,HashTbl H){  
  68.     Position p;  
  69.     List L = H->TheList[Hash(key,H->TableSize)];  
  70.     p = L->next;  
  71.     while(p != NULL && p->element != key)  
  72.         p = p->next;  
  73.     if(p == NULL)  
  74.         return L;  
  75.     else
  76.         return p;  
  77. }  
  78. void Insert(int key,HashTbl H){  
  79.     Position p,NewCell;  
  80.     p = Find(key,H);  
  81.     if(p->element != key){  
  82.         NewCell = (Position)malloc(sizeof(struct ListNote));  
  83.         if(NewCell == NULL)  
  84.             printf("分配失敗");  
  85.         else{  
  86.             p = H->TheList[Hash(key,H->TableSize)];  
  87.             NewCell->next = p->next;  
  88.             p->next = NewCell;  
  89.             NewCell->element = key;  
  90.         }  
  91.     }  
  92.     else
  93.         printf("已經存在該值了\n");  
  94. }  
  95. void Delete(int key,HashTbl H){  
  96.     Position p ,NewCell;  
  97.     p = Find(key,H);  
  98.     if(p->element == key){  
  99.         NewCell = H->TheList[Hash(key,H->TableSize)];  
  100.         while(NewCell->next != p)  
  101.             NewCell = NewCell->next;  
  102.         NewCell->next = p->next;  
  103.         free(p);  
  104.     }  
  105.     else
  106.         printf("沒有該值");  
  107. }  
  108. int main(){  
  109.     HashTbl table = InitalizeTable(10);  
  110.     Position p = NULL;  
  111.     p = Find(10,table);  
  112.     printf("%d\n",p->element);  
  113.     Insert(55,table);  
  114.     Insert(90,table);  
  115.     Insert(35,table);  
  116.     Insert(33,table);  
  117.     p = Find(55,table);  
  118.     printf("%d\n",p->element);  
  119.     p = Find(33,table);  
  120.     printf("%d\n",p->element);  
  121.     Delete(33,table);  
  122.     Delete(44,table);  
  123.     system( "pause" );  
  124.     return 0 ;  
  125. }</span>