基于线性探测解决哈希冲突的哈希表
哈希表概念
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
哈希冲突
不同的关键字通过同一哈希转换函数计算出相同的哈希地址
处理哈希冲突
常见的两种方法是闭散列和开散列
闭散列:(开放地址法或者也叫线性探测法)
当发生冲突时,如果哈希表未被填满,说明哈希表中必然有空位置,可以把key放到列表中“下一个”空位置中。
哈希表定义
7 #define max_size 1000
8
9 typedef int KeyType;
10 typedef int ValType;
11 typedef char DataType;
12 typedef int (*HashFunc)(KeyType key);
13 //哈希数组元素的状态
14 typedef enum Stat
15 {
16 Empty, //未插入
17 Valid, //插入
18 Deleted//删除
19 }Stat;
20
21 //哈希表数组中的元素类型
22 typedef struct HashElem
23 {
24 KeyType key; //包含一个键值
25 ValType value; //包含一个值
26 Stat stat; //定义哈希表中的元素状态
27 }HashElem;
28 //定义哈希表
29 typedef struct HashTable
30 {
31 HashElem data[max_size];
32 int size; //有效元素个数
33 HashFunc func; //哈希函数
34 }HashTable;
哈希表的初始化
36 //哈希表的初始化
37 void HashInit(HashTable* ht,HashFunc hash_func)
38 {
39 if(ht==NULL)
40 {
41 return ;
42 }
43 ht->size=0;
44 ht->func=hash_func;
45 int i=0;
46 for(;i<max_size;i++)
47 {
48 //将哈希表的每一个位置初始化为空状态
49 //代表相应的位置是未被使用过的
50 ht->data[i].stat=Empty;
51 }
52 }
哈希表的销毁
54 //销毁
55 void HashDestroy(HashTable *ht)
56 {
57 if(ht==NULL)
58 {
59 return ;
60 }
61 //先将表中的每一个位置都置为无效状态
62 int i=0;
63 for(;i<max_size;i++)
64 {
65 ht->data[i].stat=Empty;
66 }
67 //再将有效元素个数清0
68 ht->size=0;
69 ht->func=NULL;
70 }
哈希表中插入元素
//根据Key值计算出下标offset
//如果当前位置状态不是已插入,则插入元素,修改状态,有效个数+1
//如果当前位置状态是已插入,且key值相等,默认为不处理直接返回
//其余则认为offset更新向后循环判断是否可插
71 //插入数据
72 void HashInsert(HashTable* ht, KeyType key, ValType value)
73 {
74 if(ht==NULL)
75 {
76 return ;
77 }
78 //判断当前Hash表能否继续插入
79 //假设负载因子0.8
80 if(ht->size>=0.8max_size)
81 {
82 //当前Hash表已经达到负载因子的上限,不能继续插入
83 return;
84 }
85 //由key计算offset(由hash函数计算存放位置的下标)
86 int offset=ht->func(key);
87 //但是该位置可能之前被别的数据占据了
88 //所以要先判断计算出的位置能否放入当前数据
89 //如果不能就从offset位置往后查找
90 while(1)
91 {
92 if(ht->data[offset].stat!=Valid)
93 {
94 //如果找到的第一个位置不是有效位置
95 //可以将该数据插入
96 ht->data[offset].key=key;
97 ht->data[offset].value=value;
98 //插入完成以后将该位置置成有效状态
99 ht->data[offset].stat=Valid;
100 //哈希表有效元素个数+1
101 ++ht->size;
102 return ;
103 }
104 //走到这里说明计算出当位置不能放置待插数据
105 //判断当前位置的元素是否和待插元素一样
106 else if(ht->data[offfset].stat==Valid\
107 &&ht->data[offset].key==key)
108 {
109 //说明存在相同元素
110 //我们约定该哈希表不存在重复元素
111 //则直接插入失败返回
112 return;
113 }
114 //则更新offset值继续下一次循环往后查找
115 else
116 {
117 ++offset;
118 if(offset>=max_size)
119 {
120 //如果查找时offset走到了哈希表的末尾
121 //还没有找到一个可插入的位置
122 //则将其置为0,从头开始往后继续查找
123 offset=0;
124 }
125 }//else结束
126 }//while结束
127 }
128 //查找数据
在哈希表中查找数据
//根据Key计算出下标offset值
//如果当前位置的状态是已插入
// a.如果Key值相等,认为找到了
// b.更新offset循环继续向后查找
//如果当前位置的状态是空,则找不到
128 //查找数据
129 int HashFind(HashTable *ht, KetType key, ValType *value)
130 {
131 if(ht==NULL)
132 {
133 return 0;
134 }
135 //判断当前hash表中是否有有效元素
136 if(ht->size==0)
137 {
138 //空哈希表
139 return 0;
140 }
141 //由key值计算出offset
142 int offset=ht->func(key);
143 //从offset开始往后查找
144 while(1)
145 {
146 //在当前位置存放的是有效数据的前提下
147 if(ht->data[offset].stat==valid)
148 {
149 if(ht->data[offset].key==key)
150 {
151 //找到了
152 *value=ht->data[offset].value;
153 return 1;
154 }
155 //当前位置不是待查找的元素
156 //则更新offset的值继续查找
157 else
158 {
159 ++offset;
160 if(offset>=max_size)
161 {
162 offset=0;
163 }
164 }
165 }
166 else if(ht->data[offset].stat==empty)
167 {
168 //说明待查找的元素不存在hash表中
169 return 0;
170 }
171 }//while循环结束
172 return 0;
173 }
哈希表中删除一个元素
//根据Key值计算出下标offset
//如果当前位置状态是已插入且key值相等,则直接进行删除,修改状态
//如果当前位置状态是已插入key值不相等,更新offset循环向后寻找
//如果当前位置状态是空,则找不到删除失败
174 //删除数据
175 void HashRemove(HashTable*ht, keyType key)
176 {
177 if(ht==NULL)
178 {
179 return ;
180 }
181 if(ht->size==0)
182 {
183 //空哈希表
184 return ;
185 }
186 //由key值计算出offset
187 int offset=ht->func(key);
188 //从offset开始往后找
189 while(1)
190 {
191 if(ht->data[offset].stat==Valid\
192 &&ht->data[offset].key==key)
193 {
194 //找到了待删除的元素
195 //直接将该位置的状态置为被删除状态
196 ht->data[offset].stat=Deleted;
197 //将hash表中有效元素个数-1
198 --ht->size;
199 return ;
200 }
201 else if(ht->data[offset].stat==Empty)
202 {
203 //走到这里说明元素不存在
204 return ;
205 }
206 else
207 {
208 //走到这里说明当前offset位置的值不是我们想要删除的
209 //则更新Offset值继续查找
210 ++offset;
211 if(offset>=max_size)
212 {
213 offset=0;
214 }
215 }
216 }//while 循环结束
217 return ;
218 }
219