【经典算法实现 17】C语言 Hash 表代码实现

在前面《哈希表原理》中,
我们学习到,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; 
}

实测结果如下:
【经典算法实现 17】C语言 Hash 表代码实现_第1张图片

所有输出结果如下:

计算 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

请按任意键继续. . .

你可能感兴趣的:(数据结构与算法,链表,算法,数据结构)