哈希表(Hash table,也叫散清單),是根據關鍵碼值(Key value)而直接進行通路的資料結構。也就是說,它通過把關鍵碼值映射到表中一個位置來通路記錄,以加快查找的速度。具體的介紹網上有很詳細的描述,如 閑聊哈希表
,這裡就不再累述了;
哈希表在像Java、C#等語言中是與生俱來的。可是在C的世界中,似乎隻有自己動手,豐衣足食;在網上google了一把,大緻有幾個版本,我會一一來分析對比;
首先先來交代一下哈希表實作中需要注意的一些概念:
(主要參考:
這裡 )- 哈希函數
也叫散列函數,即:根據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 時,采用鍊位址法解決沖突的哈希表如下圖所示。

圖檔及舉例引自:
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 }
(未完待續)