雜湊表及其常用演算法(程式碼實現)
轉載自—>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 常用演算法
- 直接定址法 :地址集合 和 關鍵字集合大小相同
- 數字分析法 :根據需要hash的 關鍵字的特點選擇合適hash演算法,儘量尋找每個關鍵字的 不同點
- 平方取中法:取關鍵字平方之後的中間極為作為雜湊地址,一個數平方之後中間幾位數字與數的每一位都相關,取得位數由表長決定。比如:表長為512,=2^9,可以取平方之後中間9位二進位制數作為雜湊地址。
- 摺疊法:關鍵字位數很多,而且關鍵字中每一位上的數字分佈大致均勻的時候,可以採用摺疊法得到雜湊地址,
- 除留取餘法除P取餘,可以選P為質數,或者不含有小於20的質因子的合數
- 隨機數法:通常關鍵字不等的時候採用此法構造雜湊函式較恰當。
實際工作中需要視不同的情況採用不同的hash函式:
- 考慮因素:計算雜湊函式所需要的時間,硬體指令等因素。
- 關鍵字長度
- 雜湊表大小
- 關鍵字分佈情況
- 記錄查詢的頻率。(huffeman樹)
- <span style="font-size:16px;">#include<stdio.h>
- #include<stdlib.h>
- #include<math.h>
- struct HashTable;
- struct ListNote;
- typedefstruct HashTable *HashTbl;
- typedefstruct ListNote *Position;
- typedef Position List;
- int Hash(int key,int tablesize);
- int NextPrime(int x);
- HashTbl InitalizeTable(int TableSize);
- void DestroyTable(HashTbl H);
- Position Find(int key,HashTbl H);
- void Insert(int key, HashTbl H);
- void Delete(int key,HashTbl H);
- struct HashTable{
- int TableSize;
- Position *TheList;
- };
- struct ListNote{
- int element;
- Position next;
- };
- int Hash(int key,int tablesize){
- return key%tablesize;
- }
- int NextPrime(int x){
- int flag;
- while(1){
- flag = 0;
- int i;
- int n = sqrt((float)x);
- for(i = 2 ;i <= n;i++){
- if(x % i == 0){
- flag = 1;
- break;
- }
- }
- if(flag == 0)
- return x;
- else
- x++;
- }
- }
- HashTbl InitalizeTable(int TableSize){
- if(TableSize <= 0){
- printf("雜湊大小有問題\n");
- return NULL;
- }
- HashTbl table = (HashTbl)malloc(sizeof(struct HashTable));
- if(table == NULL)
- printf("分配失敗");
- table->TableSize = NextPrime(TableSize);
- table->TheList = (Position*)malloc(sizeof(List) * table->TableSize);
- if(table->TheList == NULL)
- printf("分配失敗");
- table->TheList[0] = (Position)malloc(table->TableSize*sizeof(struct ListNote));
- if(table->TheList == NULL)
- printf("分配失敗");
- int i;
- for(i = 0;i < table->TableSize;i++){
- table->TheList[i] = table->TheList[0] + i;
- table->TheList[i]->next = NULL;
- }
- return table;
- }
- Position Find(int key,HashTbl H){
- Position p;
- List L = H->TheList[Hash(key,H->TableSize)];
- p = L->next;
- while(p != NULL && p->element != key)
- p = p->next;
- if(p == NULL)
- return L;
- else
- return p;
- }
- void Insert(int key,HashTbl H){
- Position p,NewCell;
- p = Find(key,H);
- if(p->element != key){
- NewCell = (Position)malloc(sizeof(struct ListNote));
- if(NewCell == NULL)
- printf("分配失敗");
- else{
- p = H->TheList[Hash(key,H->TableSize)];
- NewCell->next = p->next;
- p->next = NewCell;
- NewCell->element = key;
- }
- }
- else
- printf("已經存在該值了\n");
- }
- void Delete(int key,HashTbl H){
- Position p ,NewCell;
- p = Find(key,H);
- if(p->element == key){
- NewCell = H->TheList[Hash(key,H->TableSize)];
- while(NewCell->next != p)
- NewCell = NewCell->next;
- NewCell->next = p->next;
- free(p);
- }
- else
- printf("沒有該值");
- }
- int main(){
- HashTbl table = InitalizeTable(10);
- Position p = NULL;
- p = Find(10,table);
- printf("%d\n",p->element);
- Insert(55,table);
- Insert(90,table);
- Insert(35,table);
- Insert(33,table);
- p = Find(55,table);
- printf("%d\n",p->element);
- p = Find(33,table);
- printf("%d\n",p->element);
- Delete(33,table);
- Delete(44,table);
- system( "pause" );
- return 0 ;
- }</span>