天天看點

哈希表的C實作(一)

哈希表(Hash table,也叫散清單),是根據關鍵碼值(Key value)而直接進行通路的資料結構。也就是說,它通過把關鍵碼值映射到表中一個位置來通路記錄,以加快查找的速度。具體的介紹網上有很詳細的描述,如 閑聊哈希表

 ,這裡就不再累述了;

哈希表在像Java、C#等語言中是與生俱來的。可是在C的世界中,似乎隻有自己動手,豐衣足食;在網上google了一把,大緻有幾個版本,我會一一來分析對比;

首先先來交代一下哈希表實作中需要注意的一些概念:

(主要參考:

這裡

  1. 哈希函數

也叫散列函數,即:根據key,計算出key對應記錄的儲存位置

position = f(key)

散列函數滿足以下的條件:

1、對輸入值運算,得到一個固定長度的摘要(Hash value);

2、不同的輸入值可能對應同樣的輸出值;

以下的函數都可以認為是一個散列函數:

f(x) = x mod 16; (1)

f(x) = (x2 + 10) * x; (2)

f(x) = (x | 0×0000FFFF) XOR (x >> 16); (3)

不過,僅僅滿足上面這兩條的函數,作為散列函數,還有不足的地方。我們還希望散列函數滿足下面幾點:

1、散列函數的輸出值盡量接近均勻分布;

2、x的微小變化可以使f(x)發生非常大的變化,即所謂“雪崩效應”(

Avalanche effect

);

上面兩點用數學語言表示,就是:

1, 輸出值y的分布函數F(y)=y/m, m為散列函數的最大值。或記為y~U[0, m]

2,|df(x)/dx| >> 1;

從上面兩點,大家看看,前面舉例的三個散列函數,哪個更好呢?對了,是第三個:

f(x) = (x | 0×0000FFFF) XOR (x >> 16);

它很完美地滿足“好的散列函數”的兩個附加條件。

2、哈希沖突(Hash collision)

也就是兩個不同輸入産生了相同輸出值的情況。首先,哈希沖突是無法避免的,是以,雜湊演算法的選擇直接決定了哈希沖突發送的機率;同時必須要對哈希沖突進行處理,方法主要有以下幾種:

1, 鍊位址法

鍊位址法:對Hash表中每個Hash值建立一個沖突表,即将沖突的幾個記錄以表的形式存儲在其中

2, 開放位址法

下面就來看看每種方法的具體實作吧:

鍊位址法:

舉例說明: 設有 8 個元素 { a,b,c,d,e,f,g,h } ,采用某種哈希函數得到的位址分别為: {0 , 2 , 4 , 1 , 0 , 8 , 7 , 2} ,當哈希表長度為 10 時,采用鍊位址法解決沖突的哈希表如下圖所示。 

哈希表的C實作(一)

圖檔及舉例引自:

哈希表的C實作(一)
1 #include "stdafx.h"
  2 #include <string.h>
  3 #include <stdio.h>
  4 #include <stdlib.h>
  5 
  6 typedef struct _node{
  7     char *name;
  8     char *desc;
  9     struct _node *next;
 10 }node;
 11 
 12 #define HASHSIZE 101
 13 static node* hashtab[HASHSIZE];
 14 
 15 void inithashtab(){
 16     int i;
 17     for(i=0;i<HASHSIZE;i++)
 18         hashtab[i]=NULL;
 19 }
 20 
 21 unsigned int hash(char *s){
 22     unsigned int h=0;
 23     for(;*s;s++)
 24         h=*s+h*31;
 25     return h%HASHSIZE;
 26 }
 27 
 28 node* lookup(char *n){
 29     unsigned int hi=hash(n);
 30     node* np=hashtab[hi];
 31     for(;np!=NULL;np=np->next){
 32         if(!strcmp(np->name,n))
 33             return np;
 34     }
 35     
 36     return NULL;
 37 }
 38 
 39 char* m_strdup(char *o){
 40     int l=strlen(o)+1;
 41     char *ns=(char*)malloc(l*sizeof(char));
 42     strcpy(ns,o);
 43     if(ns==NULL)
 44         return NULL;
 45     else
 46         return ns;
 47 }
 48 
 49 char* get(char* name){
 50     node* n=lookup(name);
 51     if(n==NULL)
 52         return NULL;
 53     else
 54         return n->desc;
 55 }
 56 
 57 int install(char* name,char* desc){
 58     unsigned int hi;
 59     node* np;
 60     if((np=lookup(name))==NULL){
 61         hi=hash(name);
 62         np=(node*)malloc(sizeof(node));
 63         if(np==NULL)
 64             return 0;
 65         np->name=m_strdup(name);
 66         if(np->name==NULL) return 0;
 67         np->next=hashtab[hi];
 68         hashtab[hi]=np;
 69     }
 70     else
 71         free(np->desc);
 72     np->desc=m_strdup(desc);
 73     if(np->desc==NULL) return 0;
 74     
 75     return 1;
 76 }
 77 
 78 /* A pretty useless but good debugging function,
 79 which simply displays the hashtable in (key.value) pairs
 80 */
 81 void displaytable(){
 82     int i;
 83     node *t;
 84     for(i=0;i<HASHSIZE;i++){
 85         if(hashtab[i]==NULL)
 86             printf("()");
 87         else{
 88             t=hashtab[i];
 89             printf("(");
 90             for(;t!=NULL;t=t->next)
 91                 printf("(%s.%s) ",t->name,t->desc);
 92             printf(".)");
 93         }
 94     }
 95 }
 96 
 97 void cleanup(){
 98     int i;
 99     node *np,*t;
100     for(i=0;i<HASHSIZE;i++){
101         if(hashtab[i]!=NULL){
102             np=hashtab[i];
103             while(np!=NULL){
104                 t=np->next;
105                 free(np->name);
106                 free(np->desc);
107                 free(np);
108                 np=t;
109             }
110         }
111     }
112 }
113 
114 main(){
115     int i;
116     char* names[]={"name","address","phone","k101","k110"};
117     char* descs[]={"Sourav","Sinagor","26300788","Value1","Value2"};
118     
119     inithashtab();
120     for(i=0;i<5;i++)
121         install(names[i],descs[i]);
122     
123     printf("Done");
124     printf("If we didnt do anything wrong..""we should see %s",get("k110"));
125     
126     install("phone","9433120451");
127     
128     printf("Again if we go right, we have %s and %s",get("k101"),get("phone"));
129     
130     /*displaytable();*/
131     cleanup();
132     return 0;
133 }      
哈希表的C實作(一)

(未完待續)

繼續閱讀