在前面《哈希表原理》中,
我们学习到,hash 表其实就是通过 hash函数将输入的数据进行转换成一个相对唯一的 index,
然后将数据存在 index 号的数组中,如果发现冲突,则继续往后存储。
由于hash 函数的存在,我们查找数据时,可以很快的得到它的index,
理论上它的时间复杂度是O(1),最坏的情况为O(n),也就 是所有数据都冲突。
现在我们用代码来实现一下,实现 Hash 表的增删改查,代码如下(相关注释写在代码中了):
#include
#include
// 定义Hash 表元素容量
//#define HASH_TABLE_CAPACITY 30
typedef unsigned int uint;
// Hash 表结构体定义
typedef struct Hash_Item{
struct Hash_Item *next;
char *key_string; // key
uint value; // value
}hash_item;
// 申请hash 表内存空间
void hash_init( hash_item **hi, uint length){
int i;
for(i=0; i<length; i++){
hi[i] = (hash_item *)malloc(sizeof(hash_item));
memset(hi[i], 0, sizeof(hash_item));
}
hi[0]->value = length; // 头结点用于存储hash 表容量
}
// 清空Hash 链表数据,释放内存
void hash_clear(hash_item **hi){
int i;
hash_item *p= *hi , *tmp;
printf("\n开始清空链表,释放所有内存\n");
for(i = 1; i < hi[0]->value; i++){
// 当出现index 冲突时,会创建链表,此时循环删除,直到 hi[i]->next == NULL 时就没了
while(hi[i]->next != NULL){
tmp = hi[i]->next;
hi[i]->next= tmp->next;
printf("删除[%d] %s ==> %d ,地址%p\n",i, tmp->key_string, tmp->value, tmp);
free(tmp);
}
// 此时释放 hi[i] 的内存
printf("删除hash_item[%d] ,地址%p\n",i, hi[i]);
free(hi[i]);
}
// 释放头结点
if(p != NULL){
printf("删除头结点 %d \n\n", p->value);
}
}
// 将 字符串 进行 hash 计算,得到一个整数,用于hash 键值
uint hash_string(char *key)
{
uint cal = 100;
uint hash = 0;
while(*key != '\0' && *key != 0){
hash = hash * cal + *key;
key++;
}
return hash & 0x7FFFFFFF;
}
// 获得 字符串对应的 hash 键值
uint hash_index(char *key, hash_item **hi){
// 得到哈希值
uint hash_key = hash_string(key);
// 得到哈希表容量
uint length = (hi[0]->value - 1); // 头结点不作使用
printf("\n计算 %s 的哈希值索引为 %d [%d] \n", key, hash_key, (uint)hash_key % length + 1);
return (uint)hash_key % length + 1;
}
// 获取当前 hash 表中已存元素的个数
uint hash_len(hash_item **hi){
uint length = 0, i;
for(i = 1; i<hi[0]->value; i++){
if( hi[i]->next != NULL){
length++;
}
}
return length;
}
// 打印 哈希表
void printf_hash_table(hash_item **hi){
hash_item *p;
int i;
printf("\n开始打印Hash 表内容:\n");
for(i = 1; i<hi[0]->value; i++){
p = hi[i]->next;
while(p != NULL){
printf("index[%d] %s ==> %d \n",i, p->key_string, p->value);
p = p->next;
}
}
printf("\n");
}
// 往 Hash 表中添加数据
uint hash_set(char *key, uint value, hash_item **hi){
// 将字符串计算为 哈希表 索引
uint i = hash_index(key, hi);
hash_item *p = hi[i];
// 如果 当前索引存在值
while( p->next )
{
// 如果是当前键值,则直接更新
if( strcmp(key, p->next->key_string) == 0 ){
p->next->value = value;
return 0;
}
else
{
// 如果出现冲突,即键值被占用,则创建单向链表
p = p->next;
}
}
// 开始新增数据
p->next= (hash_item *)malloc(sizeof(hash_item));
p->next->value = value;
p->next->key_string = key;
p->next->next = NULL;
return 0;
}
// 从Hash 表读取数据
hash_item* hash_get(char *key, hash_item **hi){
uint i = hash_index(key, hi);
hash_item *p = hi[i]->next;
while(p!=NULL){
if( strcmp(key, p->key_string) == 0){
return p;
}
// 如果不是当前要找的节点,找下一个,直到最终退出循环
p = p->next;
}
return NULL;
}
// 从 Hash 表中删除数据
int hash_delete(char *key, hash_item **hi){
uint i = hash_index(key,hi);
hash_item *tmp;
hash_item *p = hi[i];
while(p->next){
if(strcmp(key, p->next->key_string) == 0){
tmp = p->next;
p->next = tmp->next;
free(tmp);
return 0;
}
// 如果找不到,找下一个
p = p->next;
}
return -1;
}
int main(void){
hash_item *p;
hash_item *hash_i[100];
// 初始化hash 表
hash_init(hash_i, 100);
hash_set("Hello", 8, hash_i);
hash_set("This", 88, hash_i);
hash_set("is", 888, hash_i);
hash_set("Ciellee", 8888, hash_i);
// 打印Hash 表
printf_hash_table(hash_i);
// 查找Hash 表
printf("查找 key_string = This 的数据 :");
p = hash_get("This", hash_i);
printf(" key[%s] ==> value[%d] index=%d \n\n",p->key_string, p->value, p - *hash_i);
printf("查找 key_string = Ciellee 的数据 :");
p = hash_get("Ciellee", hash_i);
printf(" key[%s] ==> value[%d] index=%d \n\n",p->key_string, p->value, p - *hash_i);
// 修改Hash 表
hash_set("Ciellee", 9999, hash_i);
printf("修改Ciellee 为 9999 成功\n查找 key_string = Ciellee 的数据 :");
p = hash_get("Ciellee", hash_i);
printf(" key[%s] ==> value[%d] \n\n",p->key_string, p->value);
// 删除 Hash 表
printf("删除 key_string = is 的数据 \n\n");
hash_delete("is", hash_i);
// 打印Hash 表
printf_hash_table(hash_i);
// 清空链表,释放所有内存
hash_clear(hash_i);
return 0;
}
所有输出结果如下:
计算 Hello 的哈希值索引为 859639967 [99]
计算 This 的哈希值索引为 85050615 [13]
计算 is 的哈希值索引为 10615 [23]
计算 Ciellee 的哈希值索引为 9834137 [72]
开始打印Hash 表内容:
index[13] This ==> 88
index[23] is ==> 888
index[72] Ciellee ==> 8888
index[99] Hello ==> 8
查找 key_string = This 的数据 :
计算 This 的哈希值索引为 85050615 [13]
key[This] ==> value[88] index=-1431654010
查找 key_string = Ciellee 的数据 :
计算 Ciellee 的哈希值索引为 9834137 [72]
key[Ciellee] ==> value[8888] index=-1431654066
计算 Ciellee 的哈希值索引为 9834137 [72]
修改Ciellee 为 9999 成功
查找 key_string = Ciellee 的数据 :
计算 Ciellee 的哈希值索引为 9834137 [72]
key[Ciellee] ==> value[9999]
删除 key_string = is 的数据
计算 is 的哈希值索引为 10615 [23]
开始打印Hash 表内容:
index[13] This ==> 88
index[72] Ciellee ==> 9999
index[99] Hello ==> 8
开始清空链表,释放所有内存
删除hash_item[1] ,地址00BF0DD8
删除hash_item[2] ,地址00BF0DF0
删除hash_item[3] ,地址00BF0E08
删除hash_item[4] ,地址00BF0E20
删除hash_item[5] ,地址00BF0E38
删除hash_item[6] ,地址00BF0E50
删除hash_item[7] ,地址00BF0E68
删除hash_item[8] ,地址00BF0E80
删除hash_item[9] ,地址00BF0E98
删除hash_item[10] ,地址00BF0EB0
删除hash_item[11] ,地址00BF0EC8
删除hash_item[12] ,地址00BF0EE0
删除[13] This ==> 88 ,地址00BF6008
删除hash_item[13] ,地址00BF0EF8
删除hash_item[14] ,地址00BF0F10
删除hash_item[15] ,地址00BF0F28
删除hash_item[16] ,地址00BF04B0
删除hash_item[17] ,地址00BF1040
删除hash_item[18] ,地址00BF1058
删除hash_item[19] ,地址00BF10D0
删除hash_item[20] ,地址00BF0F80
删除hash_item[21] ,地址00BF10B8
删除hash_item[22] ,地址00BF10E8
删除hash_item[23] ,地址00BF0FB0
删除hash_item[24] ,地址00BF10A0
删除hash_item[25] ,地址00BF1088
删除hash_item[26] ,地址00BF0FE0
删除hash_item[27] ,地址00BF0FF8
删除hash_item[28] ,地址00BF0FC8
删除hash_item[29] ,地址00BF0F98
删除hash_item[30] ,地址00BF1010
删除hash_item[31] ,地址00BF1028
删除hash_item[32] ,地址00BF1100
删除hash_item[33] ,地址00BF1070
删除hash_item[34] ,地址00BF1118
删除hash_item[35] ,地址00BF1130
删除hash_item[36] ,地址00BF0F68
删除hash_item[37] ,地址00BF5A20
删除hash_item[38] ,地址00BF59F0
删除hash_item[39] ,地址00BF5948
删除hash_item[40] ,地址00BF5A08
删除hash_item[41] ,地址00BF5A38
删除hash_item[42] ,地址00BF59D8
删除hash_item[43] ,地址00BF5B88
删除hash_item[44] ,地址00BF5B58
删除hash_item[45] ,地址00BF5A80
删除hash_item[46] ,地址00BF5B70
删除hash_item[47] ,地址00BF5B40
删除hash_item[48] ,地址00BF5BA0
删除hash_item[49] ,地址00BF5BB8
删除hash_item[50] ,地址00BF5A50
删除hash_item[51] ,地址00BF5BD0
删除hash_item[52] ,地址00BF5BE8
删除hash_item[53] ,地址00BF5C00
删除hash_item[54] ,地址00BF5A68
删除hash_item[55] ,地址00BF5A98
删除hash_item[56] ,地址00BF5AC8
删除hash_item[57] ,地址00BF5AB0
删除hash_item[58] ,地址00BF5918
删除hash_item[59] ,地址00BF5930
删除hash_item[60] ,地址00BF5AE0
删除hash_item[61] ,地址00BF5960
删除hash_item[62] ,地址00BF5978
删除hash_item[63] ,地址00BF5990
删除hash_item[64] ,地址00BF59A8
删除hash_item[65] ,地址00BF59C0
删除hash_item[66] ,地址00BF5AF8
删除hash_item[67] ,地址00BF5B10
删除hash_item[68] ,地址00BF5B28
删除hash_item[69] ,地址00BF5C30
删除hash_item[70] ,地址00BF5CC0
删除hash_item[71] ,地址00BF5CD8
删除[72] Ciellee ==> 9999 ,地址00BF5D68
删除hash_item[72] ,地址00BF5C18
删除hash_item[73] ,地址00BF5C48
删除hash_item[74] ,地址00BF5C78
删除hash_item[75] ,地址00BF5C90
删除hash_item[76] ,地址00BF5C60
删除hash_item[77] ,地址00BF5CA8
删除hash_item[78] ,地址00BF5FF0
删除hash_item[79] ,地址00BF5F78
删除hash_item[80] ,地址00BF5F18
删除hash_item[81] ,地址00BF5DE0
删除hash_item[82] ,地址00BF5F60
删除hash_item[83] ,地址00BF5F90
删除hash_item[84] ,地址00BF5D38
删除hash_item[85] ,地址00BF5FA8
删除hash_item[86] ,地址00BF5EA0
删除hash_item[87] ,地址00BF5ED0
删除hash_item[88] ,地址00BF5E10
删除hash_item[89] ,地址00BF5F48
删除hash_item[90] ,地址00BF5D50
删除hash_item[91] ,地址00BF5E88
删除hash_item[92] ,地址00BF5DF8
删除hash_item[93] ,地址00BF5FC0
删除hash_item[94] ,地址00BF5FD8
删除hash_item[95] ,地址00BF5E58
删除hash_item[96] ,地址00BF5E40
删除hash_item[97] ,地址00BF5E28
删除hash_item[98] ,地址00BF5E70
删除[99] Hello ==> 8 ,地址00BF5EE8
删除hash_item[99] ,地址00BF5F00
删除头结点 100
请按任意键继续. . .