C数据结构与算法-基础整理-查找-02:哈希表的理解

0x01.什么是哈希表

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表

给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

存储位置=F(关键字)

F称为散列函数或哈希函数。 

采用散列技术奖记录存储在一块连续的存储空间中,这块连续的存储空间称为哈希表(或散列表)。

散列技术是一种存储方法,也是一种查找方法。

0x02.如何构造哈希函数

遵循的原则:

  • 计算简单
  • 散列地址分布均匀

常用方法:

  • 随机数法:选择一个随机函数,取关键字的随机函数值为它的哈希地址。即H(key)=random(key),其中random为随机函数。适用于关键字长度不等时。
  • 除留余数法:取关键字被某个不大于哈希表表长m的数p除后所得余数为哈希地址(p为素数)
  • 折叠法:将关键字分割成位数相同的几部分(最后一部分的位数可不同),然后取这几部分的叠加和(舍去进位)作为哈希地址。适用于关键字位数比较多,且关键字中每一位上数字分布大致均匀时。
  • 平方取中法:取关键字平方后的中间几位为哈希地址。(较常用的一种)
  • 直接定址法:取关键字或关键字的某个线性函数值为哈希地址。即H(key)=key 或 H(key)=a*key+b   (a,b为常数)。
  • 数字分析法:若关键字是以r为基的数(如:以10为基的十进制数),并且哈希表中可能出现的关键字都是事先知道的,则可取关键字的若干数位组成哈希地址。

考虑的因素:

  • 记录的查找频率
  • 哈希表的大小
  • 计算哈希函数所需时间
  • 关键字的分布情况
  • 关键字的长度

0x03.处理哈希冲突

何时冲突:

  • 当关键字值域远大于哈希表的长度,而且事先并不知道关键字的具体取值时。hash冲突就会发 生。
  • 当关键字的实际取值大于哈希表的长度时,而且表中已装满了记录,如果插入一个新记录,不仅发生冲突,而且还会发生溢出。

解决办法:

  1. 开放定址法:基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。 
  2. 链地址法:基本思想是将所有哈希地址为 i 的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。
  3. 再哈希法:当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。

0x04.哈希表应用实例

结构:

#define MAXSIZE 1000
#define NULLADR -65535
typedef struct
{
	int* adr;//元素基址
	int size;//元素个数
}HashTable;
int TableSize = 0;

建表:

void IniHashTable(HashTable* H)
{
	int i;
	TableSize = MAXSIZE;
	H->size = TableSize;
	H->adr = (int*)malloc(TableSize * sizeof(int));
	for (i = 0; i < TableSize; i++)
	{
		H->adr[i] = NULLADR;
	}
}

哈希函数:

int Hash(int key)//哈希函数
{
	return key % TableSize;//除留取余法
}

插入元素:

void InsertHash(HashTable* H, int key)
{
	int i = Hash(key);
	while (H->adr[i] != NULLADR)//开放定址法处理散列冲突
	{
		i = (i + 1) % TableSize;
	}
	H->adr[i] = key;
}

查找元素:

int Search(HashTable* H, int key)
{
	int i = Hash(key);//求散列地址
	while (H->adr[i] != key)//处理冲突
	{
		i = (i + 1) % TableSize;
	}
	if (H->adr[i] == NULLADR || i == Hash(key))//循环到了原点,查找失败
	{
		return false;
	}
	return true;
}
发布了50 篇原创文章 · 获赞 35 · 访问量 1298

猜你喜欢

转载自blog.csdn.net/ATFWUS/article/details/104461482
今日推荐