首页 > 其他分享 >哈希表

哈希表

时间:2023-02-05 00:23:55浏览次数:48  
标签:哈希 int next 链表 Item key

哈希表(hash table)又叫散列表,是一种关联式容器,存储的是一对值,一般是一个key对应一个value(又叫键值对)。数组、链表、栈、队列都是序列式容器,存储的都是一个元素。

C++ STL中的map就是一个散列表。

散列函数:

散列函数的要求:

  • hash(key)的结果是非负整数(数组下标)
  • if(key1==key2),hash(key1)==hash(key2)
  • if(key1!=key2),hash(key1)!=hash(key2)

然而实际情况中,第三点是基本做不到的,就算是著名的哈希算法MD5,SHA都难以避免会有不同key算出相同的hash值的情况,又称为哈希冲突。哈希算法有很多种,但是好的哈希算法应该尽量少地出现哈希冲突。

哈希冲突:

解决方法:开放地址法、链表法、建立公共溢出区。

开放地址法

(1)线性探测法

当我们的所需要存放值的位置被占了,我们就往后面一直加1并对m取模直到存在一个空余的地址供我们存放值,取模是为了保证找到的位置在0~m-1的有效空间之中。

存在问题:出现非同义词冲突(两个不想同的哈希值,抢占同一个后续的哈希地址)被称为堆积或聚集现象。

公式:

h(x)=(Hash(x)+i)mod (Hashtable.length)(i会逐渐递增加1)

(2)平方探测法(二次探测)

当我们的所需要存放值的位置被占了,会前后寻找而不是单独方向的寻找。

公式:

h(x)=(Hash(x) +k)mod (Hashtable.length)(k依次为+(i^2)和-(i^2))(i会逐渐递增加1)

(3)再哈希法:

同时构造多个不同的哈希函数,等发生哈希冲突时就使用第二个、第三个……等其他的哈希函数计算地址,直到不发生冲突为止。虽然不易发生聚集,但是增加了计算时间。

链表法

链表法在数组的每个槽都维护一个链表,当发生散列冲突的时候,存储在链表的结点里,插入、查找、删除的时间复杂就是链表插入、查找、删除的时间复杂度。

建立公共溢出区

将哈希表分为基本表和溢出表,将发生冲突的都存放在溢出表中。

两种方法优缺点比较

开放地址法:

优点:

所有数据都存在数组里,可以有效利用CPU缓存

所有数据都存在数组里,便于序列化

缺点:

删除需要使用特殊标志

发生冲突时插入查找删除都需要探测,代价较高

装载因子不能太大,需要提前申请好内存,这也导致了比链表法更加浪费内存

适用情况:

数据量小,装载因子小。

链表法:

优点:

需要的时候才申请结点而不是一开始就申请好,内存利用率更高

对装载因子容忍度高,就算装载因子很大,只要哈希函数分布比较平均,链表长度虽然变长,但是相当于均摊到每一条链表了,所以比起纯数组还是要快。

当数据量大时,可以使用红黑树或者跳表代替链表实现优化,使得查找效率从O(n)变成O(logn)。

缺点:

链表结点在内存中不连续,对CPU缓存不友好

需要存储额外的指针,如果存储小的对象会消耗更多的内存,如果存储较大的对象的话指针的消耗可以忽略不计。

适用情况:

存储大对象,大数据量。

装载因子

装载因子loadfactor = 填入表中的元素个数 / 散列表的长度

装载因子越大,说明散列表越满,哈希冲突的概率越大,开放地址法的探测次数增加,链表法的链表长度会增大,导致散列表的性能会下降。

一般我们会为装载因子设置一个阈值,当到达或者超过这个阈值后散列表进行动态扩容。

装载因子的阈值设置需要权衡时间,空间的需求。如果对内存充足,对执行效率性能要求比较高,可以将阈值设置小一点;如果内存紧张,对执行效率性能要求不敏感,可以将阈值设置大一些,甚至可以超过1。Java的HashMap中默认的最大装载因子是0.75。

动态扩容

上面说到装载因子达到某个阈值时,散列表需要动态扩容以减小散列冲突:申请更大的数组空间,旧数据重新计算哈希值,搬移到新的空间上。

在实际中,如果我们提供对外服务,在插入某个数据后启动动态扩容,一次性将所有旧数据计算哈希值并搬移到新空间上,可能会导致某一段时间无法响应用户的请求。

因此,在动态扩容时,我们可以先申请空间,但是先不计算哈希值和搬移数据。在需要插入新数据时,我们将新数据插入新的空间,然后从旧散列表中取一个数据计算哈希值插入新的空间里。这样的话在查找的时候也需要兼顾新旧两个散列表,先从新散列表里找,如果找不到再到旧散列表里找。

这种做法我们将动态扩容均摊到插入操作中,每次插入操作的时间复杂度是O(1),这样的做法会更加地柔和,避免了一次性动态扩容耗时过高。

实现:(链表法)

定义链表结点结构体

struct Item {
	string key;
	string value;
	Item* next;
	Item()
	{
		this->key = "empty";
		this->value = "empty";
		this->next = nullptr;
	}
	Item(string k, string v)
	{
		this->key = k;
		this->value = v;
		this->next = nullptr;
	}
};

初始化哈希表

const int tablesize = 20;
struct Item* hashtable[tablesize];
void InitHashtable()
{

	for (int i = 0; i < tablesize; ++i)
		hashtable[i] = new Item();
}

哈希函数

int hashFunction(string key)
{
	int sum = 0;
	for (int i = 0; i < key.size(); ++i)
		sum += static_cast<int>(key[i]);
	return sum % tablesize;
}

在链表中的位置

int NumOfList(string keys)
{
	int index = hashFunction(keys);
	int num = 1;
	struct Item* temp = hashtable[index]->next;
	while (temp->key != keys)
	{
		temp = temp->next;
		++num;
	}
	return num;
}

插入

这里需要注意的是,数组的槽里的结点是不存储数据的,也就是table[i]这个结点是不存储数据的,可以理解成是一个哨兵,table[i]->next所指向的结点才是第一个存储数据的。

void AddItem(string key, string value)
{
	int index = hashFunction(key);
	struct Item* temp = new Item(key, value);
	temp->next = hashtable[index]->next;
	hashtable[index]->next = temp;
}

查找

计算哈希值,然后遍历链表。

void FindValue(string key, int& index, int& num)
{
	index = hashFunction(key);
	num = NumOfList(key);
}

删除

先找这个结点,找到了就删除这个结点。

标签:哈希,int,next,链表,Item,key
From: https://www.cnblogs.com/coto/p/17092695.html

相关文章

  • 【算法】二分法 ① ( 二分法基本原理简介 | 二分法与哈希表对比 | 常见算法对应的时间
    文章目录​​一、二分法基本原理简介​​​​1、二分法与哈希表对比​​​​2、二分法具体步骤​​​​二、常见算法对应的时间复杂度​​一、二分法基本原理简介二分法算......
  • 两数之和-双指针+哈希表
    链接:两数之和题目描述给定一个整数数组nums 和一个整数目标值target,请你在该数组中找出和为目标值target 的那 两个 整数,并返回它们的数组下标。你可以假设每......
  • C++ 哈希表查询_进入哈希函数结界的世界
    1.前言哈希表或称为散列表,是一种常见的、使用频率非常高的数据存储方案。哈希表属于抽象数据结构,需要开发者按哈希表数据结构的存储要求进行API定制,对于大部分高级语言......
  • openwrt内核模块怎样解决哈希依赖问题
    openwrt内核模块怎样解决哈希依赖问题 来源 https://forum.gl-inet.cn/forum.php?mod=viewthread&tid=1032&extra=page%3D1参考 https://forum.gl-inet.cn/forum.php?......
  • 【LeetCode哈希表#3】快乐数(set)
    快乐数力扣题目链接(opensnewwindow)编写一个算法来判断一个数n是不是快乐数。「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后......
  • 哈希表
    概述什么的等我有空再补。用的时候直接开一个myhasha,然后像普通的unordered_map一样a[x]就可以了(&[]重定义的功劳)。structmyhash{ structdata{ ullk......
  • Python中得可变哈希不可变哈希
    类型与哈希哈希(散列计算),可以将任意长度的输出,通过散列算法变为固定长度输出,简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。​​1.可哈希......
  • MySQL 哈希索引、空间数据索引、全文索引
    1.哈希索引哈希索引基于哈希表实现,仅支持精确匹配索引所有列的查询。对于每行数据,存储引擎都会对所有的索引列计算出一个哈希码。哈希索引将所有的哈希码存储在索引中,同时保......
  • 关于AC自动机的一些理解 || Luogu3121 & 4824 Censoring - 哈希 - AC自动机
    题目链接:https://www.luogu.com.cn/problem/P3121(4824)题解:4824是CensoringS,只需要对单模式串进行操作,3121需要对多模式串4824开一个前缀hash数组,每次扫到当前点......
  • 常用哈希质数
    61,83,113,151,211,281,379,509683,911/一千以下1217,1627,2179,2909,3881,......