哈希以及闭散列和开散列

news2024/9/21 5:37:16

哈希

  • 一、哈希
    • 1、概念
    • 2、哈希冲突
  • 二、哈希函数
    • 1、设计原则
    • 2、常见的哈希函数
      • (1)直接定址法
      • (2)除留余数法
      • (3)平方取中法
      • (4)折叠法
      • (5)随机数法
      • (6)数学分析法
    • 3、注意
  • 三、闭散列
    • 1、概念
    • 2、操作
    • 3、线性探测
      • (1)操作
      • (2)优点与缺点
      • (3)插入元素操作示意图
    • 4、二次探测
      • (1)操作
      • (2)优点与缺点
    • 5、示例代码
    • 6、扩容
  • 四、开散列
    • 1、操作
    • 2、插入元素操作示意图
    • 3、扩容

一、哈希

1、概念

  • 哈希是一种把任意长度的输入通过散列算法变换成固定长度的输出的方法,也称为散列、杂凑。在计算机科学中,哈希数据结构是一种非常重要的数据存储和检索方法。
  • 哈希函数将数据的关键值映射到一个特定的位置,从而实现快速的数据查找、插入和删除。
  • 使用哈希构造出来的结构称为哈希(散列)表(Hash Table)。

2、哈希冲突

  • 不同关键字通过相同哈希函数计算出相同的哈希地址,进而导致不同的值映射到相同的位置上,即为哈希冲突或哈希碰撞。
  • 把具有不同关键码而具有相同哈希地址的数据元素称为同义词。
  • 解决哈希冲突两种常见的方法是闭散列和开散列。

二、哈希函数

1、设计原则

  • 哈希函数的定义域必须包括需要存储的全部关键码,即如果散列表允许有m个地址,其值域必须在0到m-1之间。
  • 哈希函数计算出来的地址需能均匀分布在整个空间中。
  • 哈希函数应该比较简单。

2、常见的哈希函数

(1)直接定址法

  • 取关键字的某个线性函数为散列地址。
  • 优点为简单、均匀。关键字和存储位置之间是一一对应的,不存在哈希冲突。
  • 缺点为需要事先知道关键字的分布情况。如果数据很分散,会导致所开的空间很大,即浪费资源。
  • 使用场景为关键字范围集中、数量少且连续的情况。

(2)除留余数法

  • 设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址。
  • 使用场景为关键字分散、数量多。关键字和存储位置之间是多对一的关系,存在哈希冲突。

(3)平方取中法

  • 取关键字平方值的中间三位数字作为哈希地址。
  • 假设关键字为1234,它的平方值是1522756,取中间的3位227作为哈希地址。
  • 假设关键字为4321,它的平方值是18671041,取中间的3位671或者710作为哈希地址。
  • 平方取中法适合用于不知道关键字的分布,而位数又不是很大的情况。

(4)折叠法

  • 将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些),然后将这几部分叠加求和,并按散列表表长,取后几位作为散列地址。
  • 折叠法适合用于不知道关键字的分布,位数比较多的情况。

(5)随机数法

  • 选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key) = random(key),其中random为随机数函数。
  • 随机数法通常用于关键字长度不相等的情况。

(6)数学分析法

  • 设有n个d位数,每一位可能有r种不同的符号,这r种不同的符号在各位上出现的频率不一定相同,可能在某些位上分布比较均匀,每种符号出现的机会均等,在某些位上分布不均匀只有某几种符号经常出现。可根据散列表的大小,选择其中各种符号分布均匀的若干位作为散列地址。
  • 数字分析法通常适合处理关键字位数比较大,事先知道关键字的分布且关键字的若干位分布较均匀的情况。

3、注意

哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲突。

三、闭散列

1、概念

  • 闭散列是一种预先分配存储空间的方法。 在闭散列中,存储空间被划分为若干个固定大小的桶,每个桶中可以存储多个元素。 当插入新元素时,根据其哈希值确定其所属的桶,并将元素添加到该桶中。 如果发生哈希冲突,则将元素添加到下一个可用的桶中。
  • 这种方法简单易行,但可能会导致某些桶被过度使用,而其他桶却是空闲的。即空间利用率比较低。

2、操作

  • 采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索。所以,可以采用标记的伪删除法删除元素。
  • 当查找特定元素时,需要查找到表位置为空为止,避免发生冲突的值查找不到。当再插入数据时,标记为删除的位置可以重新覆盖值。

3、线性探测

(1)操作

  • 在散列表中,每个单元存储一个元素。 当哈希函数对一个元素产生一个值时,这个值会指向散列表中的某个单元。 如果这个单元已经被另一个元素占用,就会发生冲突。
  • 查找散列表中离冲突单元最近的空闲单元,并将新的元素插入到这个空闲单元中。当发生冲突时,算法会按照一定的步长(通常为1)逐个检查邻近的单元,直到找到一个空闲单元或者达到某个终止条件(如到达散列表的末尾或者最开始冲突的位置)。

(2)优点与缺点

  • 优点为实现简单。
  • 缺点为一旦发生哈希冲突,所有的冲突连在一起,容易产生数据堆积。即不同的元素占据了可利用的空位置,使得寻找某元素的位置需要进行多次比较,即搜索效率降低。

(3)插入元素操作示意图

在这里插入图片描述

4、二次探测

(1)操作

  • 二次探测的方法是根据一个增量序列(通常是1,2,3,……)来探测相邻的位置,直到找到一个空闲位置。
  • 使用一个哈希函数将元素映射到哈希表中的一个位置。如果该位置已经被占用,则使用二次探测的方法来寻找一个空闲位置。
  • 如果探测到哈希表的末尾仍然没有找到空闲位置,则需要使用再散列的方法,即重新计算哈希值,并使用新的哈希值来查找空闲位置。

(2)优点与缺点

  • 优点为二次探测法通过跳跃式探测避免了连续位置的占用,从而减少了聚集现象的发生;能够较好地保持哈希表的负载因子,提高哈希表的查找和插入效率。
  • 缺点为当哈希表较满时,二次探测法的探测过程可能会变长,甚至可能探测失败(即所有可能的位置都被占用)。
  • 与线性探测法相比,二次探测法在实现上较为复杂。

5、示例代码

enum State { EMPTY, EXIST, DELETE };

template<class K, class V>
class HashTable
{
	struct Elem
	{
		pair<K, V> _val;
		State _state = EMPTY;
	};
public:
	HashTable(size_t capacity = 10)
		: _ht(capacity), _size(0)
	{}
	bool Insert(const pair<K, V>& val)
	{
		if (Find(val.first))
			return false;
		if (_size * 10 / _ht.size() >= 7)
		{
			HashTable<K, V> newht;
			size_t newsize = _ht.size() * 2;
			newht._ht.resize(newsize);
			for (auto data : _ht)
			{
				if (data._state == EXIST)
				{
					newht.Insert(data._val);
				}
			}
			Swap(newht);
		}
		//如果K值类字符串等需要转换的,需实现一个仿函数HashFunc
		//HashFunc<K> hf;
		//size_t hashi = hf(val.first) % _ht.size();
		
		size_t hashi = val.first % _ht.size();
		
		size_t index = hashi;
		size_t i = 1;
		while (_ht[index]._state == EXIST)
		{
			index = hashi + i;
			index %= _ht.size();
			++i;
		}
		_ht[index]._val = val;
		_ht[index]._state = EXIST;
		_size++;

		return true;
	}
	Elem* Find(const K& key)
	{
		//如果K值类字符串等需要转换的,需实现一个仿函数HashFunc
		//HashFunc<K> hf;
		//size_t hashi = hf(key) % _ht.size();

		size_t hashi = key % _ht.size();

		size_t i = 1;
		size_t index = hashi;
		while ( _ht[index]._state != EMPTY)
		{
			if (_ht[index]._state == EXIST
				&& _ht[index]._val.first == key)
				return &_ht[index];
			index = hashi + i;
			index %= _ht.size();
			++i;
			if (index == hashi)
				break;
		}
		return nullptr;
}
private:
	vector<Elem> _ht;
	size_t _size;
};
  • 代码为线性探测,二次探测只需修改相应的i和index的值即可。

6、扩容

  • 随着在散列表中插入的元素增多,表中空闲的位置也会相应的减少。当表插满时,插入元素的操作就不能正常插入元素了,这时就需要进行扩容。
  • 散列表是否需要扩容可以用一个载荷因子来判断。它的定义为,α = 填入表中的元素个数/散列表的长度。α是散列表装满程度的标志因子。由于表长是定值,α与填入表中的元素个数成正比。所以,α越大表明填入表中的元素越多,产生冲突的可能性就越大,而效率就会降低。反之,α越小表明填入表中的元素越少,产生冲突的可能性就越小,但空间利用率就会降低。
  • 实际上,散列表的平均查找长度是载荷因子α的函数,只是不同处理冲突的方法有不同的函数。对于开放定址法,载荷因子是特别重要的因素,应严格限制在0.7-0.8以下。当其超过0.8时,查表时的CPU缓存不命中(cache missing)按照指数曲线上升。

四、开散列

1、操作

  • 开散列法又称为链地址法(开链法),其首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个哈希桶,各个哈希桶中的元素通过一个单链表链接起来,各链表(哈希桶)的头结点存储在哈希表中。
  • 开散列中每个桶中存放的都是发生哈希冲突的元素。

2、插入元素操作示意图

在这里插入图片描述

3、扩容

  • 因为哈希表中桶的个数是一定的,随着元素的不断插入,每个桶中元素的个数会不断增多。在极端情况下,可能会导致插入元素都在一个桶中,即该桶中链表节点非常多,这会影响哈希表的性能,因此在一定条件下需要对哈希表进行扩容。
  • 开散列最好的情况是每个哈希桶中刚好有一个节点,再继续插入元素时,每一次都会发生哈希冲突。因此,在元素个数刚好等于桶的个数时,可以给哈希表扩容。

更多哈希相关的内容参见位图、布隆过滤器和哈希切割

本文到这里就结束了,如有错误或者不清楚的地方欢迎评论或者私信
创作不易,如果觉得博主写得不错,请点赞、收藏加关注支持一下💕💕💕

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2041251.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

期权快到期卖不出去了怎么办 ?

期权在最后交易日如果平仓卖不出去&#xff0c;说明没有市场参与者愿意以你的报价买入该期权。这种情况通常出现在期权价值极低&#xff0c;接近于0&#xff0c;或者期权已经深度虚值&#xff0c;即行权价远离当前市场价格而且剩余时间已非常有限&#xff0c;使得该期权的内在价…

vue选中下拉框数据,但是值没有填充到框内

​​ 下拉框选中数据后&#xff0c;roomType的值没有自动更新 查找资料后的已解决&#xff0c;特此记录。 解决办法&#xff1a; 加入强制更新时间 <el-form-item label"房型" prop"roomType"><el-select v-model"form.roomType" pla…

RAG问答系统|QAnything:多类型文本的知识库,安全可靠、一键离线部署

转自老贾探AI 在日常生活和工作中&#xff0c;我们经常面对大量本地信息资料&#xff08;如PDF、Doc等&#xff09;&#xff0c;需要进行关联理解和处理。频繁地切换和区分文件格式&#xff0c;再加上需要跨语言理解的环境&#xff0c;使得如何找到一个准确、快速、可靠地处理文…

48天笔试训练错题——day48

目录 选择题 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 编程题 1. 左右最值最大差 2. 顺时针打印矩阵 选择题 1. cookie 是浏览器存储数据的机制&#xff0c;是用于维护 http 通信当中客户端的用户信息的。 2. 403 Forbidden 表示资源不可用&#xff0c;服务器理解客户请…

前端案例:酷我音乐项目(移动端自适应)(HTML+CSS)

一个简单的移动端案例&#xff0c;模拟不同设备下逻辑分辨率不同&#xff0c;宽高自适应 正常打开整体布局 打开 f12 &#xff08;ctrl shift M&#xff09;或者点击左上角图标,将其模拟为移动端设备 在移动设备iPhone6/7/8&#xff0c;逻辑分辨率375的整体布局 头部和底部的…

虚幻5|角色武器装备的数据库学习(不只是用来装备武器,甚至是角色切换也很可能用到)

虚幻5|在连招基础上&#xff0c;给角色添加武器并添加刀光|在攻击的时候添加武器并返回背后&#xff08;第一部分&#xff0c;下一部分讲刀光&#xff09;_unreal 如何给角色添加攻击-CSDN博客 目的&#xff1a;捡起各种不同的武器&#xff0c;捡起的武器跟装备的武器相匹配 …

C语言日常练习 Day15

目录 一、猴子吃桃问题。 二、两个乒乓球队进行比赛&#xff0c;各出3个人。甲队为A&#xff0c;B&#xff0c;C3人&#xff0c;乙队为X&#xff0c;Y,Z3人。已抽签决定比赛名单。有人向队员打听比赛的名单&#xff0c;A说他不和X比赛&#xff0c;C说他不和X&#xff0c;Z比赛…

Java爬虫中的数据清洗:去除无效信息的技巧

在互联网信息爆炸的时代&#xff0c;数据的获取变得异常容易&#xff0c;但随之而来的是数据质量的问题。对于Java爬虫开发者来说&#xff0c;如何从海量的网页数据中清洗出有价值的信息&#xff0c;是一个既基础又关键的步骤。本文将介绍Java爬虫中数据清洗的重要性&#xff0…

VBA自动发邮件如何配置SMTP实现外部发送?

VBA自动发邮件的教程&#xff1f;如何利用VBA自动化发送邮件&#xff1f; 在使用VBA进行自动化办公任务时&#xff0c;自动发送邮件是一项非常实用的功能。AokSend将详细介绍如何通过VBA自动发邮件&#xff0c;并配置SMTP实现外部发送的过程。 VBA自动发邮件&#xff1a;准备…

前端开发攻略---彻底弄懂跨域解决方案

目录 1、浏览器的同源策略 1.1 源 1.2 同源与非同源 1.3 同源请求与非同源请求 2、跨域受到的限制 3、注意点 4、CORS解决Ajax跨域问题 4.1 CORS概述 4.2 CORS解决简单请求跨域 4.3 简单请求与复杂请求 4.4 CORS解决复杂请求跨域 4.5 借助CORS库快速完成配置 5、JS…

Daiqile SQL注入绕过

上源码。 <?php header("Content-type: text/html; charsetutf-8"); require db.inc.php;function dhtmlspecialchars($string) {if (is_array($string)) {foreach ($string as $key > $val) {$string[$key] dhtmlspecialchars($val);}}else {$string str_…

【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇 本篇为大家分享下在C学习中较为具有挑战与难度&#xff0c;同时也是很重要知识。掌握C类的六个默认成员函数&#xff0c;使得在模拟实现STL中容器过程得心应…

企业必备:2024年顶尖10款人事管理系统

本篇文章介绍了以下几个工具&#xff1a;Moka、卓望ShineHR、华夏HR云、中智人事、i人事、北森iTalentX、红海云、Zenefits、ICE Hrm、ADempiere。 在选择合适的人事管理系统时&#xff0c;很多企业面临如何找到既可靠又能满足特定需求的平台的难题。每个系统都有其独特之处&am…

前端工程化-03.环境准备

一.前端工程化 既然要实现前端工程化&#xff0c;那就要使用一些现成的工具来帮助我们实现&#xff0c;这个工具就是vue官方提供的脚手架 首先就要下载安装vue脚手架 二.什么是vue脚手架 三.安装NodeJS 1.先安装NodeJS才能安装vue脚手架。 Node.js — 在任何地方运行 Jav…

window.onload、$(document).ready()、Vue.created() 页面加载完成后执行方法

1、JavaScript 的 window.onload 方法 window.onload 方法是在页面所有元素&#xff08;包括图片、样式、链接等&#xff09;加载完成后触发的&#xff0c;在这个事件之前&#xff0c;页面上的所有资源都必须加载完成。因此&#xff0c;如果页面中包含大量的图片或其他资源&am…

【科目结转】财务科目结转

*&---------------------------------------------------------------------* *&程序名称 &#xff1a;ZFI134 *&程序描述 : 9003差异科目结转 &#xff08;批量操作 F.02 / 查询 FB03) *&申请单位 …

qt quick实现的水波纹特效:横向波纹、纵向波纹效果

qml实现的水波纹特效 1.横向波纹效果2.另一种效果&#xff08;纵向波纹&#xff09; 一直以来使用c qt如果要实现一些高级特效比如水波纹效果都难度比较大&#xff0c;但是使用qt quick难度就会小很多。这里借鉴一些网友的思路简单实现一下水波纹效果。主要思路就是波浪的形成是…

Aigtek高压放大器在无线电能传输的应用范围

无线电能传输是一种重要的技术&#xff0c;广泛应用于电力、通信和工业领域。高压放大器作为无线电能传输系统中的关键组件之一&#xff0c;扮演着放大信号、提高传输效率的重要角色。 无线电能传输是一种将电能通过无线电波或磁场从发送器传输到接收器的技术。它可以实现远距离…

web自动化测试Day4

目标 下拉选择框&#xff1b;弹出框&#xff1b;滚动条操作&#xff1b;frame表单切换&#xff1b;多窗口切换&#xff1b;窗口截图、验证码处理 定位下拉框 select选择框 下标从0开始 #通过下标形式访问 #通过value值形式访问 注意事项 实例化select时候&#xff0c;需要…

如何选择正规的调度控制台厂家?

在现代社会&#xff0c;随着各行各业对高效、安全管理的需求日益增长&#xff0c;调度控制台作为监控与指挥的核心设备&#xff0c;其重要性不言而喻。然而&#xff0c;市场上调度控制台厂家众多&#xff0c;产品质量与服务水平参差不齐&#xff0c;如何从中挑选出正规、可靠的…