【C/C++】哈希

2023-11-10

1.unordered系列关联式容器

1.1unordered_map接口
  • 在内部,unordered_map没有对<kye, value>按照任何特定的顺序排序, 为了能在常数范围内找到key所对应的value,unordered_map将相同哈希值的键值对放在相同的桶中。

  • unordered_map是存储<key, value>键值对的关联式容器,其允许通过keys快速的索引到与其对应的value

unordered_map的容量

bool empty()const 		检测unordered_map是否为空
size_t size() const 	返回unordered_map的有效元素个数

unordered_map的迭代器

begin()		返回unordered_map的第一个元素的迭代器
end()		返回unordered_map最后一个元素下一个位置的迭代器
cbegin()	返回unordered_map第一个元素的const迭代器
cend()		返回unordered_map最后一个元素下一个位置的const迭代器

unordered_map的元素访问

operator[]	返回与key对应的value

unordered_map的查询

iterator find(const k&key)  返回key在哈希桶的位置
size_t count(const k&key)	返回哈希桶中关键码key的键值对的个数

unordered_map的修改操作

insert		向容器中插入键值对
    pair<iterator,bool>insert(const value_type& val)
erase		删除容器中的键值对
    iterator erase(const_iterator position)
    size_type erase(const key_type& k)
    iterator erase(const_iterator first,const_iterator last)

unordered_map操作

size_t bucket_count()const 返回哈希桶中的桶个数
size_t bucket_size(size_t n)const	返回n号桶有效元素的总个数
size_t bucket(const k& key) 返回元素key所在的桶号
1.2unordered_set

接口与unordered_map相似

2.底层原理

unordered系列的关联式容器之所以效率高,是因为其底层使用了hashtable结构。

2.1顺式结构和平衡树

元素关键码key与其存储位置之间没有对应的关系,因此在查找一个元素时,需要进行多次关键码的比较。顺序结构的查找的时间复杂度O(N),平衡树中位数的高度,即O(logn)。

2.2hash结构

可以不经过任何比较,而是通过hash函数,一次直接从表中得到需要搜索的元素。

  • 插入元素:根据待插入元素的关键码,通过hash函数得到存储的位置
  • 搜索元素:对元素的关键码进行计算,得到关键码对应的存储位置。
  • hash中使用的转换函数叫做哈希函数,构造出来的结构叫做hashtable

例如:将数据集合映射在hash表中{1,7,6,4,5,9}

在这里插入图片描述

2.3哈希冲突【哈希碰撞】

对于两个不同的关键字key1和key2,通过哈希函数可能映射在同一个位置:即Hash(key1)==Hash(key2);该现象称为哈希冲突或哈希碰撞

2.4合理的哈希函数

引起哈希冲突的一个原因:哈希函数设计不合理

解决哈希冲突的方法:闭散列和开散列

**负载因子:**数组存储元素的个数/数组的长度;负载因子越大,哈希冲突越大,负载因子越小,哈希冲突越小。

2.4.1常见哈希函数
  • 各种字符串哈希函数的比较链接:

各种字符串Hash函数 - clq - 博客园 (cnblogs.com)

  • 双重哈希

​ 在.net HashTable类的hash函数Hk定义如下:
​ Hk(key) = [GetHash(key) + k * (1 + (((GetHash(key) >> 5) + 1) %
(hashsize – 1)))] % hashsize;
​ 在此 (1 + (((GetHash(key) >> 5) + 1) % (hashsize – 1))) 与 hashsize互为素数(两数互为素数表示两者没有共同的质因⼦);
​ 执⾏了 hashsize 次探查后,哈希表中的每⼀个位置都有且只有⼀次被访问到,也就是说,对于给定的 key,对哈希表中的同⼀位置不会同时使⽤ Hi 和 Hj;

2.5闭散列【开发定址法】

​ 闭散列:也叫开放定址法。当发送哈希冲突时,如果哈希表未被装满,说明哈希表中必然还要空位置,那么就可以把key存放在其他没有填放的位置。找未填充的位置也有两种方法:1.线性探测 2.二次探测

2.5.1线性探测

发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。

**解决哈希冲突的方法:i+1,i+2,i+3,i+4 … i+n **

在这里插入图片描述

2.5.3线性探测哈希结构的实现

封装哈希数据

enum state
{
	EMPTY,
	EXITS,
	DELETE
};
//封装Hash对应的数据
template<class K, class V>
struct HashData
{
	pair<K, V> _kv;
	state _state = EMPTY;
};

封装减小哈希碰撞的仿函数

template <class K>
struct DefultHash
{
	size_t operator()(const K& k)
	{
		return (size_t)k;
	 }
};
//模板特化,特化的类型是string类
template<>
struct DefultHash<string>
{
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (auto i : key)
		{
			hash = hash * 131 + i;
		}
		return (size_t)hash;
	}
};

封装哈希表【线性探测】

template<class K,class V,class HashFunc=DefultHash<K>>
class HashTable
{
public:
	typedef HashData<K, V> Data;
	//支持修改的查找函数
	Data* find(const K&key)
	{
		if (_table.size() == 0)
		{
			return nullptr;
		 }
		HashFunc func;
		size_t starti = func(key);
		starti %= _table.size();
		size_t hashi = starti;
		size_t i = 1;
		while (_table[hashi]._state != EMPTY)
		{
			if (_table[hashi]._state != DELETE &&_table[hashi]._kv.first == key)
			{
				return &_table[hashi];
			}
			hashi = starti + i;
			i++;
			hashi %= _table.size();
		}
		return nullptr;
	}
	bool insert(const pair<K,V>& kv)
	{
		//判断是否存在
		if (find(kv.first))
		{
			return false;
		}
		//判断是否需要扩容:如果vector为空,或者负载因子过大,就进行扩容
		if (_table.size()==0||n*10/_table.size()>7)
		{
			size_t newsize = _table.size() == 0 ? 10 : _table.size() * 2;
			//创建新的HashTable
			HashTable<K, V,HashFunc> _newtable;
			_newtable._table.resize(newsize);
			//将原来的数据插入到新的哈希表中
			for (auto& e : _table)
			{
				if (e._state != EMPTY)
				{
					if (e._state == EXITS)
					{
						_newtable.insert(e._kv);
					}
				}
			}
			_newtable._table.swap(_table);
		}
		//插入数据
		HashFunc func;
		size_t hashi = func(kv.first);
		hashi %= _table.size();
		size_t starti = hashi;
		size_t i = 1;
		//线性探测
		while (_table[hashi]._state == EXITS)
		{
			hashi = starti + i;
			i++;
			hashi %= _table.size();
		}
		_table[hashi]._kv = kv;
		_table[hashi]._state = EXITS;
		n++;
		return true;
	}
	bool erase(const K& key)
	{
		Data* ret = find(key);
		if (ret)
		{
			ret->_state = DELETE;
			n--;
			return true;
		}
		return false;
	}
private:
	vector<Data> _table;
	size_t n = 0; //存储的关键字个数
};
2.5.2二次探测

解决哈希冲突的方法:i-1^2 ,i+2^2 ,i-3^2 ,1+4^2 …

​ 这两种探测方式都会导致同类hash聚集;也就是近似值它的hash值也近似,那么它的数组槽位也靠近,形成hash聚集;第⼀种同类聚集冲突在前,第⼆种只是将聚集冲突延后;

​ 二次探测的实现:只需要将hashi = starti + i修改为hashi = starti + flagi * i;flag=-1flag即可。

2.6开散列【哈希桶】

开散列

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

哈希的链式结构

​ 在Hash Table下面挂一个桶,桶中存的是发生哈希冲突的元素。

​ 数据量少的时候可以挂链表,数据量大的时候可以挂RBTree。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ge7dqfHJ-1663235810203)(C:/Users/%E5%88%98%E9%91%AB/AppData/Roaming/Typora/typora-user-images/image-20220907005914062.png)]

2.7开散列实现哈希结构

我们在哈希表的下面挂上链表存放数据。其他操作和闭散列的实现方式相似。

具体的实现可见码云:哈希 · 影中人/mylx - 码云 - 开源中国 (gitee.com)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C/C++】哈希 的相关文章

  • UTF8/UTF16 和 Base64 在编码方面有什么区别

    In c 我们可以使用下面的类来进行编码 System Text Encoding UTF8 System Text Encoding UTF16 System Text Encoding ASCII 为什么没有System Text En
  • 在 LINQ 查询中返回不带时间的日期

    我正在编写一个查询 我想计算按日期联系我们的呼叫中心的次数 看起来很简单 但由于联系日期字段是日期时间字段 我得到了时间 因此当我按联系日期 时间 分组时 每个联系日期实例的计数为 1 所以 我想只按日期分组 而不按时间分组 下面是我用来查
  • 创建 DirectoryEntry 实例以供测试使用

    我正在尝试创建 DirectoryEntry 的实例 以便可以使用它来测试将传递 DirectoryEntry 的一些代码 然而 尽管进行了很多尝试 我还是找不到实例化 DE 并初始化它的 PropertyCollection 的方法 我有
  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • 如何在C++中实现模板类协变?

    是否可以以这样一种方式实现类模板 如果模板参数相关 一个对象可以转换为另一个对象 这是一个展示这个想法的例子 当然它不会编译 struct Base struct Derived Base template
  • FFMPEG Seeking 带来音频伪影

    我正在使用 ffmpeg 实现音频解码器 在读取音频甚至搜索已经可以工作时 我无法找到一种在搜索后清除缓冲区的方法 因此当应用程序在搜索后立即开始读取音频时 我没有任何工件 avcodec flush buffers似乎对内部缓冲区没有任何
  • c# Asp.NET MVC 使用FileStreamResult下载excel文件

    我需要构建一个方法 它将接收模型 从中构建excel 构建和接收部分完成没有问题 然后使用内存流导出 让用户下载它 不将其保存在服务器上 我是 ASP NET 和 MVC 的新手 所以我找到了指南并将其构建为教程项目 public File
  • 当 Cortex-M3 出现硬故障时如何保留堆栈跟踪?

    使用以下设置 基于 Cortex M3 的 C gcc arm 交叉工具链 https launchpad net gcc arm embedded 使用 C 和 C FreeRtos 7 5 3 日食月神 Segger Jlink 与 J
  • 基于范围的 for 循环中的未命名循环变量?

    有没有什么方法可以不在基于范围的 for 循环中 使用 循环变量 同时也避免编译器发出有关未使用它的警告 对于上下文 我正在尝试执行以下操作 我启用了 将警告视为错误 并且我不想进行像通过在某处毫无意义地提及变量来强制 使用 变量这样的黑客
  • 为什么模板不能位于外部“C”块内?

    这是一个后续问题一个答案 https stackoverflow com questions 4866433 is it possible to typedef a pointer to extern c function type wit
  • 在 ASP.Net Core 2.0 中导出到 Excel

    我曾经使用下面的代码在 ASP NET MVC 中将数据导出到 Excel Response AppendHeader content disposition attachment filename ExportedHtml xls Res
  • 更改窗口的内容 (WPF)

    我创建了一个简单的 WPF 应用程序 它有两个 Windows 用户在第一个窗口中填写一些信息 然后单击 确定 这会将他们带到第二个窗口 这工作正常 但我试图将两个窗口合并到一个窗口中 这样只是内容发生了变化 我设法找到了这个更改窗口内容时
  • .NET 选项将视频文件流式传输为网络摄像头图像

    我有兴趣开发一个应用程序 它允许我从 xml 构建视频列表 包含视频标题 持续时间等 并将该列表作为我的网络摄像头流播放 这意味着 如果我要访问 ustream tv 或在实时通讯软件上激活我的网络摄像头 我的视频播放列表将注册为我的活动网
  • 用 C 实现 Unix shell:检查文件是否可执行

    我正在努力用 C 语言实现 Unix shell 目前正在处理相对路径的问题 特别是在输入命令时 现在 我每次都必须输入可执行文件的完整路径 而我宁愿简单地输入 ls 或 cat 我已经设法获取 PATH 环境变量 我的想法是在 字符处拆分
  • AccessViolationException 未处理

    我正在尝试使用史蒂夫 桑德森的博客文章 http blog stevensanderson com 2010 01 28 editing a variable length list aspnet mvc 2 style 为了在我的 ASP
  • 什么是 C 语言的高效工作流程? - Makefile + bash脚本

    我正在开发我的第一个项目 该项目将跨越多个 C 文件 对于我的前几个练习程序 我只是在中编写了我的代码main c并使用编译gcc main c o main 当我学习时 这对我有用 现在 我正在独自开展一个更大的项目 我想继续自己进行编译
  • 作为字符串的动态属性名称

    使用 DocumentDB 创建新文档时 我想设置属性名称动态地 目前我设置SomeProperty 像这样 await client CreateDocumentAsync dbs db colls x new SomeProperty
  • 在Linux中使用C/C++获取机器序列号和CPU ID

    在Linux系统中如何获取机器序列号和CPU ID 示例代码受到高度赞赏 Here http lxr linux no linux v2 6 39 arch x86 include asm processor h L173Linux 内核似
  • 将变量分配给另一个变量,并将一个变量的更改反映到另一个变量中

    是否可以将一个变量分配给另一个变量 并且当您更改第二个变量时 更改会瀑布式下降到第一个变量 像这样 int a 0 int b a b 1 现在 b 和 a 都 1 我问这个问题的原因是因为我有 4 个要跟踪的对象 并且我使用名为 curr
  • 为什么 strtok 会导致分段错误?

    为什么下面的代码给出了Seg 最后一行有问题吗 char m ReadName printf nRead String s n m Writes OK char token token strtok m 如前所述 读取字符串打印没有问题 但

随机推荐

  • Python模块学习 ---- atexit

    atexit模块很简单 只定义了一个register函数用于注册程序退出时的回调函数 我们可以在这个回调函数中做一些资源清理的操作 注 如果程序是非正常crash 或者通过os exit 退出 注册的回调函数将不会被调用 我们也可以通过sy
  • 一篇文章让你搞定所有redis面试题

    Redis是什么 Redis是C语言开发的一个开源的 遵从BSD协议 高性能键值对 key value 的内存数据库 可以用作数据库 缓存 消息中间件等 它是一种NoSQL not only sql 泛指非关系型数据库 的数据库 redis
  • Arduino酸度计(PH计)

    在本项目中 我们将通过将模拟pH传感器与Arduino接口来设计pH计 介绍 在化学中 pH是用于指定水基溶液的酸性或碱性的标度 酸性溶液的pH值较低 而碱性溶液的pH值较高 因此 Ph传感器具有确定任何溶液的Ph的能力 即可以判断该物质本
  • JAVA运行时类存在,但是报错:NoClassDefFoundError: Could not initialize class

    我们在部署代码时 明明类存在 但是发现报错 NoClassDefFoundError Could not initialize class 这类问题是由静态成员或静态初始化语句块引起 我们先看下面个类 import org apache c
  • C语言实现MD5/SHA1/SHA256/SHA512

    哈希函数是我们做校验时经常会用到的密码学工具 目前常用的工具有MD5 SHA1 SHA256 SHA512等 其中MD5已经被证实不安全 目前只能作为一种辅助的校验手段 而不能防篡改 下面介绍如何使用mbedTLS协议栈中的hash代码生成
  • BGP属性

    BGP 外部网关协议 此协议不在于自动发现网络拓扑 不追求速度 而在于AS之间选择最佳路由和控制路由的传播 追求可靠性 稳定性 操控性 承载性 使用TCP作为其传输协议 监听端口号为179 保证其可靠性 路由更新只发送更新的路由 适用于在以
  • C++基础学习笔记——对象的定义及引用

    1 类与对象的关系 通常我们把具有同样性质和功能的东西所构成的集合称为类 在C 中 可以把相同内部存储结构和相同操作集的对象看成属于同一类 在C 中 对象是类的实际变量 类与对象间的关系 可以用整型 int 和整型变量 i 之间的关系来类比
  • Linux——线程1

    一 线程基础 进程 有独立的进程地址空间 有独立的pcb 线程 有独立的pcb 没有独立的进程地址空间 因此进程线程最本质的区别就是 是否共享地址空间 在Linux下线程是最小的执行单位 进程是最小的分配资源单位 可看成只有一个线程的进程
  • 避坑记录:打电话(uni.makePhoneCall)

    uni makePhoneCall 可兼容微信小程序 H5 移动端 安卓 IOS 但是在移动端 安卓 上 如果拒绝授权电话 则会出现点击号码 既不报错 也不弹出打电话的bug 当然 如果只是简单调用makePhoneCall 也就不值得我去
  • Call Exec in PeopleCode

    我想在Application Engine里加一段调用命令行的代码 All PeopleCode is executed on the application server So if you re calling an interacti
  • 基于imx6ull视频监控

    基于imx6ull视频监控 前言 一 mjpg streamer 1 编译mjpg streamer 2 运行mjpg 3 mjpg框架 二 流媒体 1 ffmpeg 2 nginx服务器 3 实现flv js访问和ip地址访问 4 内网穿
  • MySQL添加用户、删除用户与授权

    前言 MySql中添加用户 新建数据库 用户授权 删除用户 修改密码 注意每行后边都跟个 表示一个命令语句结束 新建用户 登录MYSQL mysql u root p 密码 创建用户 mysql gt insert into mysql u
  • 从iOS App启动速度看如何为基础性能保驾护航

    1 前言 启动是App给用户的第一印象 一款App的启动速度 不单单是用户体验的事情 往往还决定了它能否获取更多的用户 所以到了一定阶段App的启动优化是必须要做的事情 App启动基本分为以下两种 1 1 冷启动 App 点击启动前 它的进
  • python:深拷贝,浅拷贝,赋值引用

    第一部分转载自 https www cnblogs com xueli p 4952063 html 1 python的复制 深拷贝和浅拷贝的区别 在python中 对象赋值实际上是对象的引用 当创建一个对象 然后把它赋给另一个变量的时候
  • purrr 0.2.0

    purrr 0 2 0 Hadley Wickham 2016 01 06 Categories Packages tidyverse 原文地址 我很高兴的发布了purrr 0 2 0 Purrr填补了R的函数式编程工具中的缺失部分 让你的
  • rpm包的卸载与安装

    本文章向大家介绍rpm包的卸载与安装 主要内容包括1 rpm包管理 2 rpm包的简单查询指令 3 卸载rpm包 4 安装rpm包 使用实例 应用技巧 基本知识点总结和需要注意事项 具有一定的参考价值 需要的朋友可以参考一下 目录 1 rp
  • 模式识别课程:目标检测③基于深度学习的检测算法

    title 目标检测 基于深度学习的检测算法 目标检测实验报告 检测所用软硬件 云服务器 硬件 macOS或者windows电脑 软件 pycharm 生成的测试集 云服务器 滴滴云 https www didiyun com activi
  • redisHyperLogLog原理解析

    场景 做服务端的同学 应该都遇到过计数场景 比如我想知道浏览某一个web页面的总人数 总次数 查看某条热门动态的总人数总次数 购买某件商品的总人数总次数 对于总次数我们直接基于计数器累加就能很方便的解决 时间和空间复杂度都不高 而对于总人数
  • fix: Build warning "generate id 'android:id/xxx' for external package 'android'

    other ref https blog csdn net w1070216393 article details 83088054 attr file
  • 【C/C++】哈希

    文章目录 1 unordered系列关联式容器 1 1unordered map接口 1 2unordered set 2 底层原理 2 1顺式结构和平衡树 2 2hash结构 2 3哈希冲突 哈希碰撞 2 4合理的哈希函数 2 4 1常见