数据结构与算法——哈希表
1. 什么是哈希表
首先有这么一种情况,有24个人编号分别为1~24,我们需要将 24 人均分成 6 个组!
编号除 6 余数为 0 的为第零组: 6、12、18、24
编号除 6 余数为 1 的为第一组: 1、7、13、19
编号除 6 余数为 2 的为第二组: 2、8、14、20
编号除 6 余数为 3 的为第三组: 3、9、15、21
编号除 6 余数为 4 的为第四组: 4、10、16、22
编号除 6 余数为 5 的为第五组: 5、11、17、23
通过这种编号方式划分队列,寻找某一个数会非常方便,比如寻找19,19 % 6 = 1,19再第一组。这种编号的方式就是高效的散列,我们俗称 “哈希”!
以上过程是通过把关键码值 key(编号)映射到表中一个位置(数组的下标)来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
哈希表 - 散列表,它是基于快速存取的角度设计的,也是一种典型的 “空间换时间” 的做法
键(key): 组员的编号 如, 1 、 5 、 19 。 。 。
值(value): 组员的其它信息(包含 性别、年龄和战斗力等)
索引: 数组的下标(0,1,2,3,4) ,用以快速定位和检索数据
哈希桶: 保存索引值的数组(或链表),数组(或链表)成员为每一个索引值相同的多个元素(以链表的形式链接)的首节点
哈希函数: 将组员编号映射到索引上,采用求余法 ,如: 组员编号 19
下面是代码实现,先贴一张运行结果
2. 哈希表的算法实现
2.1 哈希表数据结构的定义
1 #define DEFAULT_SIZE 16 2 3 typedef struct _ListNode 4 { 5 struct _ListNode *next; 6 int key; 7 void *data; 8 }ListNode; 9 10 typedef ListNode *List; 11 typedef ListNode *Element; 12 13 typedef struct _HashTable 14 { 15 int TableSize; 16 List *Thelists; 17 }HashTable;
2.2 哈希函数
1 /*根据 key 计算索引,定位 Hash 桶的位置*/ 2 int Hash(int key, int TableSize) 3 { 4 //return (key % TableSize); 5 return (key % 6); 6 }
2.3 哈希链表初始化
1 /*初始化哈希表*/ 2 HashTable* InitHash(int TableSize) 3 { 4 int i = 0; 5 HashTable* hTable = NULL; 6 if (TableSize <= 0) 7 { 8 TableSize = DEFAULT_SIZE; 9 } 10 hTable = (HashTable*)malloc(sizeof(HashTable)); 11 if (NULL == hTable) 12 { 13 printf("HashTable malloc error.\n"); 14 return NULL; 15 } 16 hTable->TableSize = TableSize; 17 18 //为一连窜的 Hash 桶首地址分配内存空间,其为一个指针数组 19 hTable->Thelists = (List*)malloc(sizeof(List) * TableSize); 20 if (NULL == hTable->Thelists) 21 { 22 printf("HashTable malloc error\n"); 23 free(hTable); 24 return NULL; 25 } 26 27 //为每一个 Hash 桶内对应的指针数组初始化链表节点 28 for (i = 0; i < TableSize; i++) 29 { 30 hTable->Thelists[i] = (ListNode*)malloc(sizeof(ListNode)); 31 if (NULL == hTable->Thelists[i]) 32 { 33 printf("HashTable malloc error\n"); 34 free(hTable->Thelists); 35 free(hTable); 36 return NULL; 37 } 38 else 39 { 40 memset(hTable->Thelists[i], 0, sizeof(ListNode)); 41 } 42 } 43 return hTable; 44 }
2.4 哈希链表插入元素
1 /*哈希表插入元素,元素为键值对*/ 2 void Insert(HashTable* HashTable, int key, void* value) 3 { 4 Element e = NULL, tmp = NULL; 5 List L = NULL; 6 e = Find(HashTable, key); 7 if (NULL == e) 8 { 9 tmp = (Element)malloc(sizeof(ListNode)); 10 if (NULL == tmp) 11 { 12 printf("malloc error\n"); 13 return; 14 } 15 //定位哈希桶 16 L = HashTable->Thelists[Hash(key, HashTable->TableSize)]; 17 tmp->data = value; 18 tmp->key = key; 19 tmp->next = L->next; //前插法, 将新元素插到哈希桶的前部 20 L->next = tmp; 21 } 22 else 23 { 24 //如果元素存在,不做修改 25 //printf("the key already exist\n"); 26 27 //如果Key存在,修改Key的值 28 e->data = value; 29 } 30 }
2.5 哈希链表查找元素
1 /*从哈希表中根据键值查找元素*/ 2 Element Find(HashTable *HashTable, int key) 3 { 4 int i = 0; 5 List L = NULL; 6 Element e = NULL; 7 i = Hash(key, HashTable->TableSize); 8 L = HashTable->Thelists[i]; 9 e = L->next; 10 while (e != NULL && e->key != key) 11 e = e->next; 12 return e; 13 }
2.6 哈希链表删除元素
1 /*哈希表删除元素,元素为键值对*/ 2 void Delete(HashTable *HashTable, int key ) 3 { 4 Element e=NULL, last=NULL; 5 List L=NULL; 6 int i = Hash(key, HashTable->TableSize); 7 L = HashTable->Thelists[i]; 8 last = L; 9 e = L->next; 10 11 while (e != NULL && e->key != key) 12 { 13 last = e; 14 e = e->next; 15 } 16 if(e) //如果键值对存在 17 { 18 last->next = e->next; 19 delete(e); 20 } 21 }
3. 完整源码实现(链式存储)
hash_table.h
1 #pragma once 2 3 #define DEFAULT_SIZE 16 4 5 /*哈希表元素定义*/ 6 typedef struct _ListNode 7 { 8 struct _ListNode* next; 9 int key; 10 void* data; 11 }ListNode; 12 13 typedef ListNode* List; 14 typedef ListNode* Element; 15 16 /*哈希表结构定义*/ 17 typedef struct _HashTable 18 { 19 int TableSize; 20 List* Thelists; 21 }HashTable; 22 23 /*哈希函数*/ 24 int Hash(void* key, int TableSize); 25 26 /*初始化哈希表*/ 27 HashTable* InitHash(int TableSize); 28 29 /*哈希表插入*/ 30 void Insert(HashTable* HashTable, int key, void* value); 31 32 /*哈希表查找*/ 33 Element Find(HashTable* HashTable, int key); 34 35 /*哈希表销毁*/ 36 void Destory(HashTable* HashTable); 37 38 /*哈希表元素中提取数据*/ 39 void* Retrieve(Element e);
hash_table.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include "hash_table.h" 5 6 /*根据 key 计算索引,定位 Hash 桶的位置*/ 7 int Hash(int key, int TableSize) 8 { 9 //return (key % TableSize); 10 return (key % 6); 11 } 12 13 /*初始化哈希表*/ 14 HashTable* InitHash(int TableSize) 15 { 16 int i = 0; 17 HashTable* hTable = NULL; 18 if (TableSize <= 0) 19 { 20 TableSize = DEFAULT_SIZE; 21 } 22 23 24 hTable = (HashTable*)malloc(sizeof(HashTable)); 25 if (NULL == hTable) 26 { 27 printf("HashTable malloc error.\n"); 28 return NULL; 29 } 30 hTable->TableSize = TableSize; 31 32 //为一连窜的 Hash 桶首地址分配内存空间,其为一个指针数组 33 hTable->Thelists = (List*)malloc(sizeof(List) * TableSize); 34 if (NULL == hTable->Thelists) 35 { 36 printf("HashTable malloc error\n"); 37 free(hTable); 38 return NULL; 39 } 40 41 //为每一个 Hash 桶内对应的指针数组初始化链表节点 42 for (i = 0; i < TableSize; i++) 43 { 44 hTable->Thelists[i] = (ListNode*)malloc(sizeof(ListNode)); 45 if (NULL == hTable->Thelists[i]) 46 { 47 printf("HashTable malloc error\n"); 48 free(hTable->Thelists); 49 free(hTable); 50 return NULL; 51 } 52 else 53 { 54 memset(hTable->Thelists[i], 0, sizeof(ListNode)); 55 } 56 } 57 return hTable; 58 } 59 60 /*从哈希表中根据键值查找元素*/ 61 Element Find(HashTable* HashTable, int key) 62 { 63 int i = 0; 64 List L = NULL; 65 Element e = NULL; 66 i = Hash(key, HashTable->TableSize); 67 L = HashTable->Thelists[i]; 68 e = L->next; 69 70 while (e != NULL && e->key != key) 71 e = e->next; 72 73 return e; 74 } 75 76 /*哈希表插入元素,元素为键值对*/ 77 void Insert(HashTable* HashTable, int key, void* value) 78 { 79 Element e = NULL, tmp = NULL; 80 List L = NULL; 81 e = Find(HashTable, key); 82 if (NULL == e) 83 { 84 tmp = (Element)malloc(sizeof(ListNode)); 85 if (NULL == tmp) 86 { 87 printf("malloc error\n"); 88 return; 89 } 90 //定位哈希桶 91 L = HashTable->Thelists[Hash(key, HashTable->TableSize)]; 92 tmp->data = value; 93 tmp->key = key; 94 tmp->next = L->next; //前插法, 将新元素插到哈希桶的前部 95 L->next = tmp; 96 } 97 else 98 { 99 //如果元素存在,不做修改 100 //printf("the key already exist\n"); 101 102 //如果Key存在,修改Key的值 103 e->data = value; 104 } 105 106 107 } 108 109 /*哈希表删除元素,元素为键值对*/ 110 void Delete(HashTable* HashTable, int key) 111 { 112 Element e = NULL, last = NULL; 113 List L = NULL; 114 int i = Hash(key, HashTable->TableSize); 115 L = HashTable->Thelists[i]; 116 last = L; 117 e = L->next; 118 119 while (e != NULL && e->key != key) 120 { 121 last = e; 122 e = e->next; 123 } 124 if (e) //如果键值对存在 125 { 126 last->next = e->next; 127 free(e); 128 } 129 } 130 131 /*哈希表元素中提取数据*/ 132 void* Retrieve(Element e) 133 { 134 return e ? e->data : NULL; 135 } 136 137 /*销毁哈希表*/ 138 void Destory(HashTable* HashTable) 139 { 140 int i = 0; 141 List L = NULL; 142 Element cur = NULL, next = NULL; 143 for (i = 0; i < HashTable->TableSize; i++) 144 { 145 L = HashTable->Thelists[i]; 146 cur = L->next; 147 while (cur != NULL) 148 { 149 next = cur->next; 150 free(cur); 151 cur = next; 152 } 153 free(L); 154 } 155 free(HashTable->Thelists); 156 free(HashTable); 157 } 158 159 void main(void) 160 { 161 char* elems[] = 162 { 163 "字符串1","字符串2","字符串3","字符串4","字符串5","字符串6", 164 "字符串7","字符串8","字符串9","字符串10","字符串11","字符串12", 165 "字符串13","字符串14","字符串15","字符串16","字符串17","字符串18", 166 "字符串19","字符串20","字符串21","字符串22","字符串23","字符串24" 167 }; 168 169 HashTable* HashTable; 170 HashTable = InitHash(24); 171 172 for (int i = 0; i < HashTable->TableSize; i++) 173 { 174 Insert(HashTable, i + 1, elems[i]); 175 } 176 177 for (int i = 1; i <= HashTable->TableSize; i++) 178 { 179 Element e = Find(HashTable, i); 180 if (e) 181 { 182 printf("K为:[%d]", i); 183 printf("所在哈希桶:[%d] ,参数:", i % 6); 184 printf("%s\n", (const char*)Retrieve(e)); 185 } 186 else 187 { 188 printf("Not found [key:%d]\n", i); 189 } 190 } 191 192 printf("\n\n删除Key能被5整除的内容\n\n"); 193 for (int i = 0; i <= HashTable->TableSize; i++) 194 { 195 if (i != 0 && i % 5 == 0) 196 { 197 Delete(HashTable, i); 198 } 199 } 200 201 for (int i = 1; i <= HashTable->TableSize; i++) 202 { 203 Element e = Find(HashTable, i); 204 if (e) 205 { 206 printf("K为:[%d]", i); 207 printf("所在哈希桶:[%d] ,参数:", i % 6); 208 printf("%s\n", (const char*)Retrieve(e)); 209 } 210 else 211 { 212 printf("Not found [key:%d]\n", i); 213 } 214 } 215 216 printf("\n\n将Key能被3整除的内容改为:888888\n\n"); 217 for (int i = 0; i <= HashTable->TableSize; i++) 218 { 219 if (i != 0 && i % 3 == 0) 220 { 221 Insert(HashTable, i, "888888"); 222 } 223 } 224 225 for (int i = 1; i <= HashTable->TableSize; i++) 226 { 227 Element e = Find(HashTable, i); 228 if (e) 229 { 230 printf("K为:[%d]", i); 231 printf("所在哈希桶:[%d] ,参数:", i % 6); 232 printf("%s\n", (const char*)Retrieve(e)); 233 } 234 else 235 { 236 printf("Not found [key:%d]\n", i); 237 } 238 } 239 240 system("pause"); 241 }
================================================================================================================================