哈希表

本文介绍了哈希表的基本概念及其实现方法,探讨了如何通过哈希函数快速定位元素,并讨论了解决哈希冲突的策略,包括线性再探测和再哈希等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

哈希表,又称为散列,是一种更加快捷的查找技术。我们之前的查找,都是这样一种思路:集合中拿出来一个元素,看看是否与我们要找的相等,如果不等,缩小范围,继续查找。而哈希表是完全另外一种思路:当我知道key值以后,我就可以直接计算出这个元素在集合中的位置,根本不需要一次又一次的查找!是不是很神奇呢?其实一点也不神奇,举一个例子就明白了,假如我的数组A中,第i个元素里面装的key就是i,那么数字3肯定是在第3个位置,数字10肯定是在第10个位置。这不是废话么……哈希表就是利用利用这种基本的思想,建立一个从key到位置的函数,然后进行直接计算查找。

但是实际应用中,和可能出现一种问题:就是多个key被映射成为同一个位置。比如:我想用人的名字来映射人的时候,就会出现两个人的名字汉语拼音相同的情况。那么这种情况又该如何处理你?有两种大的思路:

1.建立一个缓冲区,把凡是拼音重复的人放到缓冲区中。当我通过名字查找人时,出现找的不对,就在缓冲区里找。

2.进行再探测。就是在其他地方查找。探测的方法也可以有很多种。

(1)在找到查找位置的index的index-1,index+1位置查找,index-2,index+2查找,依次类推。这种方法称为线性再探测。

(2)在查找位置index周围随机的查找。称为随机在探测。

(3)再哈希。就是当冲突时,采用另外一种映射方式来查找。

基本思想就是这么多,下面我们看看程序:

#include <stdio.h>
#include <malloc.h>

typedef struct HashTable
{
	char* data;
	int count;
	int size;
}HashTable,*pHashTable;

extern  int counter;
bool initHashTable(pHashTable  ,int );
void destroyHashTable(pHashTable );
void fillHashTable(pHashTable  ,char* , int );
int Hash(char );
bool HashSerach(pHashTable , char , int* );


 

#include "hash.h"

bool initHashTable(pHashTable pH ,int n)
{
	pH->count = 0;
	pH->size = n;
	pH->data = (char*)malloc(sizeof(char) * n);
	//起始值为NULL
	for(int i = 0; i < n;++i)
	{
		pH->data[i] = '*';
	}
	if(NULL == pH->data)
		return false;
	else
		return true;
}

void destroyHashTable(pHashTable pH)
{
	pH->count = 0;
	pH->size = 0;
	free(pH->data);
	pH->data= NULL;

}

//Hash函数就是简单地取模
int Hash(char key)
{
	return (int)key % 7;
}

void fillHashTable(pHashTable pH ,char* key, int n)
{
	int tmp;
	for(int i = 0; i < n; ++i)
	{
		tmp = Hash(key[i]);
		//如果这个位置没有人用过,那么就把键填过去
		if('*' == pH->data[tmp])
			pH->data[tmp] = key[i];
		else
		{
			//直到找到一个没人用过的位置
			while('*' != pH->data[tmp])
			{
				//跳过7个位置,再次判断有没有被使用
				tmp = (tmp + 7) % pH->size;//防止越界

			}
			//如果没有,则把值填过去
			pH->data[tmp] = key[i];
		}
		++pH->count;
	}
}

//使用全局变量来记录运算次数
int counter = 0;
bool HashSerach(pHashTable pH, char key, int* index)
{
	int tmp = Hash(key);
	//直接找到
	++counter;
	if(pH->data[tmp] == key)
	{
		
		*index = tmp;
		return true;
	}
	else
	{
		while(true)
		{
			
			//增加7个元素,再次尝试
			tmp += 7;
			++counter;
			if(pH->data[tmp] == key)
			{				
				*index = tmp;
				return true;
			}
			//tmp越界,表明没有找到
			if(tmp > 30)
			{
				*index = -1;
				return false;
			}
		}
	
	}
	

}


最后看看主函数:

#include "hash.h"

int main(void)
{
	HashTable h;
	initHashTable(&h,30);
	char test = 'a';
	char arr[26];
	for(int i = 0; i< 26;++i)
	{
		arr[i] = test + i;
	}
	fillHashTable(&h,arr,26);
	for(int i = 0; i < h.size;++i)
		printf("%c",h.data[i]);
	printf("\n");
	int index = 100;
	
	for(int i = 0; i < 26;++i)
	{
		HashSerach(&h,test+i,&index);
		if(index >= 0)
			printf("find %c\n",h.data[index]);
		else
			printf("can't find the char");	
	}
	printf("总的查找次数为%d",counter);
	destroyHashTable(&h);
	return 0;
}


 

这个程序中是通过取模来模拟查找到重复元素的过程。对待重复元素的方法就是再哈希:对当前key的位置+7。最后,可以通过全局变量来判断需要查找多少次。我这里通过依次查找26个英文字母的小写计算的出了总的查找次数。显然,当总的查找次数/查找的总元素数越接近1时,哈希表更接近于一一映射的函数,查找的效率更高。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值