当前位置: 首页 > news >正文

C++ unordered_map和unordered_set的使用,哈希表的实现

文章目录

  • unordered_map,unorder_set和map ,set的差异
  • 哈希表的实现
    • 概念
    • 直接定址法
    • 哈希冲突
      • 哈希冲突举个例子
    • 负载因子
    • 将关键字转为整数
    • 哈希函数
      • 除法散列法/除留余数法
    • 哈希冲突的解决方法
      • 开放定址法
        • 线性探测
        • 二次探测
    • 开放定址法代码实现
  • 哈希表的代码

unordered_map,unorder_set和map ,set的差异

unordered_map,unordered_set在功能方面和map,set基本一致
unordered_map和unordered_set底层是哈希表,遍历无序,查找删除修改的效率为O(1)
map和set底层是红黑树,遍历有序,查找删除修改的效率为O(logN)

  1. 功能:迭代器之间也有差异,set是双向迭代器,unordered_set是单向迭代器,set底层是红黑树,走中序是有序的,所以迭代器的遍历是有序+去重unordered_set底层是哈希表,迭代器遍历是无序+去重
  2. 效率:unordered_set和set性能方面也有差异,set是O(logN),unordered_set是O(1)
  3. 使用要求:对key的要求不同,set要求key支持小于的比较,unordered_set要求key转为整形且要支持等于的比较
  4. unordered_set,unordered_map和map,set要求基本一致
  5. unordered_multimap和unordered_multiset支持键值冗余(key冗余)
  6. 效率上无序的比有序的总体上要好很多,但是在排有序的数的时候,有序的删除,插入比无序的效率高,但是查找效率还是无序的效率高
    在这里插入图片描述
void set_test1()
{// 迭代器遍历set<int> s = { 3,1,6,7,8,2};set<int>::iterator it = s.begin();while (it != s.end()){cout << *it << " ";++it;}// 3 1 6 7 8 2cout << endl;
}int main()
{set_test1();return 0;
}

哈希表的实现

概念

  • 哈希表也叫散列表,散列有散乱排列的意思。通过关键字Key跟存储位置建立一个映射关系

直接定址法

  • 适合关键字的范围比较集中的,直接定址法是非常高效的方法。比如给’a’ - 'z’的小写字母为范围,通过直接映射或相对映射关键字的值就是存储位置的下标,下面也有一道题是这样的。只适合整形,字符串,浮点数都不行

字符串中的第一个唯一字符

class Solution 
{
public:int firstUniqChar(string s) {int hash[26] = {0};for(auto ch : s){hash[ch - 'a']++;}    for(int i = 0;i < s.size();i++){if(hash[s[i] - 'a'] == 1)return i;}return -1;}
};

哈希冲突

  • 直接定址法的缺点非常明显,当关键字比较分散时,会浪费空间甚至空间会不够用。当有N个值要映射到M个空间中(M >= N),就要借助哈希函数,关键字key要放到数组的h(key)的位置,h(key)计算的值要在[0,M)中。
  • 哈希冲突也叫哈希碰撞,就是两个不同的key映射到同一个位置了,我们要设计一个哈希函数来减少哈希冲突,在实际问题中哈希冲突是不可避免的

哈希冲突举个例子

N == 100 M == 200,N个数要存到M个空间中
除法散列法:h(key) = key % M
哈希冲突:3 % 200 = 3,203 % 200 = 3

负载因子

哈希表中已经存了N个值,哈希表的大小为M,负载因子 = N/M,负载因子也叫装载因子/载荷因子,负载因子越大,哈希冲突越高,空间利用率越高,相反就越低。

将关键字转为整数

比如是浮点数转为整数或者有符号的整数(负数)要转为正数,字符串转为整数。

哈希函数

一个好的哈希函数应该让N个关键字被等概率的均匀的散列分布到哈希表的M个空间中,但是实际中却很难做到,但是我们要尽量往这个方向去考量设计。

除法散列法/除留余数法

  • 除法散列表是最常用的,哈希表的大小为M,通过key除以M的余数作为映射的下标,h(key) = key % M
  • 使用这个方法时要注意M不要为2的幂,10的幂等。如果是2^X,那么key%2 ^ X,相当于保留key的后X位,那么key的后X位相同的值就哈希冲突了
  • 比如63,31,如果M是16,2 ^ 4,计算出的哈希值都是15,00111111为63,00011111为31,后4位都是1111,所以会哈希冲突。10进制的,比如123和345123,如果M = 10 ^ 3,保留后三位,都是123,也哈希冲突。
  • 当使用除留余数法时,建议M取不太接近2的整数次幂的一个质数
  • %其实用到了除,除的效率比位运算的效率更低,所以Java中用了位运算

Java中用了2的整数次幂作为哈希表的大小M
在这里插入图片描述

哈希冲突的解决方法

开放定址法

  • 开放定址法,哈希表是空的,把所有元素放到哈希表中,当关键字key用哈希函数找到了冲突位置,就按照某种规则去找一个没有存储数据的位置进行储存。这里有三种规则:线性探测,二次探测,双重探测
线性探测

现在给一组数据:
{19,30,5,36,13,20,21,12} 等这一组值映射到M=11的表中

h(19) = 8,h(30) = 8,h(5) = 5,h(36) = 3,
h(13) = 2,h(20) = 9,h(21) =10,h(12) = 1
在这里插入图片描述
上面这组数据在8位置就冲突了

  • 线性探测:从发生冲突的位置依次往后探测,直到找到一个没有存储数据的位置为止,如果走到哈希尾都没有找到,则回绕到哈希头继续往后找
  • h(key) = hash0 = key % M , hash0位置冲突了,则线性探测公式为:hc(key, i) = hashi = (hash0 + i) % M, i = {1, 2, 3, …, M − 1},因为负载因子必须小于1(因为要留一个位置不放数据,不然会出问题),则最多探测M-1次,一定能找到一个存储key的位置。
  • 连续冲突:hash0位置连续冲突,(你占我的位置,我占你的位置)这种现象叫做 群集/堆积,二次探测可以改善这样的问题
二次探测

二次探测和线性探测非常类似
如果往左走回绕到哈希表尾,用减,比如3-9,到5的位置停下,-6 + M,M == 11, -6 + M = 5
在这里插入图片描述

// 二次探测
int flag = 1;
while (_tables[hashi]._state == EXIST)
{// 存在进行二次探测,删除和空就退出hashi = (hash0 + i*i*flag) % _tables.size();if (hashi < 0)hashi += _tables.size();if (flag == 1){flag = -1;}else{flag = 1;++i;}
}

开放定址法代码实现

h(19) = 8,h(30) = 8,h(5) = 5,h(36) = 3,
h(13) = 2,h(20) = 9,h(21) =10,h(12) = 1
蓝色的四个位置连续冲突在这里插入图片描述
查找的原则:遇到删除,存在才继续找,遇到空就结束查找
状态标识:存在,空和删除,空和删除可以放值,空就线性探测
要注意给每个存储值的位置加一个状态标识,否则删除一些值以后,会影响后面冲突的值的查找。
如下图,我们删除30,会导致查找20失败,当我们给每个位置加⼀个状态标识{EXIST,EMPTY,DELETE} ,删除30就可以不用删除值,而是把状态改为 DELETE ,那么查找20时是遇到 EMPTY 才能停止,就可以找到20。

哈希表的代码

#pragma once#include<vector>// 枚举状态
enum State
{EXIST,EMPTY,DELETE
};template<class K,class V>
struct HashData
{pair<K, V> _kv;State _state = EMPTY;
};template<class K,class V>
class HashTable
{
public:// 构造HashTable()// 会取到53:_tables(__stl_next_prime(0)),// 11是数据个数()_n(0){}// 为了解决M是质数的问题inline unsigned long __stl_next_prime(unsigned long n){// Note: assumes long is at least 32 bits.static const int __stl_num_primes = 28;static const unsigned long __stl_prime_list[__stl_num_primes] = {53, 97, 193, 389, 769,1543, 3079, 6151, 12289, 24593,49157, 98317, 196613, 393241, 786433,1572869, 3145739, 6291469, 12582917, 25165843,50331653, 100663319, 201326611, 402653189, 805306457,1610612741, 3221225473, 4294967291};const unsigned long* first = __stl_prime_list;const unsigned long* last = __stl_prime_list + __stl_num_primes;const unsigned long* pos = lower_bound(first, last, n);// lower_bound取表中大于等于first的数return pos == last ? *(last - 1) : *pos;}// 插入bool Insert(const pair<K, V>& kv){// 不支持冗余if (Find(kv.first)){return false;}// 负载因子大于等于0.7就扩容if (_n * 10 / _tables.size() >= 7){// 扩容//vector<HashTable<K, V>> newtables(_tables.size()*2);//for (auto& data : _tables)//{//	// 把旧表的数据映射到新表//	// 不能直接拷贝,因为映射关系还是原来的(会乱)//	if (data._state == EMPTY)//	{//		size_t hash0 = data._kv.first % newtables.size();//		// ...//	}//}//_tables.swap(newtables);// 上面的方法代码过于冗余// 把新表和旧表交换HashTable<K, V> newht;// newht._tables.resize(_table.size() * 2);newht._tables.resize(__stl_next_prime(_table.size()));for (auto& data : _tables){// 把旧表的数据映射到新表if (data._state == EMPTY){// 相当于递归newht.Insert(data._kv);}}// 函数结束,newht销毁,数据交换给旧表_tables.swap(newht._tables);}// key / M , M哈希表的空间大小size_t hash0 = kv.first % _tables.size();size_t hashi = hash0;size_t i = 1;while (_tables[hashi]._state == EXIST){// 存在进行线性探测,删除和空就退出hashi = (hash0 + i) % _tables.size();++i;}// 二次探测//int flag = 1;//while (_tables[hashi]._state == EXIST)//{//	// 存在进行二次探测,删除和空就退出//	hashi = (hash0 + i*i*flag) % _tables.size();//	if (hashi < 0)//		hashi += _tables.size();//	if (flag == 1)//	{//		flag = -1;//	}//	else//	{//		flag = 1;//		++i;//	}//}_tables[hashi]._kv = kv;_tables[hashi]._state = EXIST;++_n;return true;}// 查找HashData<K, V>* Find(const K& key){size_t hash0 = key % _tables.size();size_t hashi = hash0;size_t i = 1;// Find等于空就找不到了while (_tables[hashi]._state != EMPTY){if (_tables[hashi]._state == EXIST&& _tables[hashi]._kv.first == key){return &_tables[hashi];}// 存在进行线性探测,删除和空就退出hashi = (hash0 + i) % _tables.size();++i;}return nullptr;}// 删除bool Erase(const K& key){HashData<K, V>* ret = Find(key);if (key){ret->_state = DELETE;return true;}else{return false;}}private:vector<HashData<K, V>> _tables;size_t _n;// 记录哈希表中的数据个数
};#include"HashTable.h"int main()
{int a[] = { 19,30,5,36,13,20,21,12 };HashTable<int, int> ha;for (auto& e : a){ha.Insert({ e,e });}ha.Erase(20);if (ha.Find(30)){cout << "找到了" << endl;}if (ha.Find(20)){cout << "找到了" << endl;}else{cout << "没有找到" << endl;}return 0;
}

相关文章:

C++ unordered_map和unordered_set的使用,哈希表的实现

文章目录 unordered_map&#xff0c;unorder_set和map &#xff0c;set的差异哈希表的实现概念直接定址法哈希冲突哈希冲突举个例子 负载因子将关键字转为整数哈希函数除法散列法/除留余数法 哈希冲突的解决方法开放定址法线性探测二次探测 开放定址法代码实现 哈希表的代码 un…...

games101-作业3

由于此次试验需要加载模型&#xff0c;涉及到本地环节&#xff0c;如果是windows系统&#xff0c;需要对main函数中的路径稍作改变&#xff1a; 这么写需要&#xff1a; #include "windows.h" 该段代码&#xff1a; #include "windows.h" int main(int ar…...

【Block总结】高效多尺度注意力EMA,超越SE、CBAM、SA、CA等注意力|即插即用

论文信息 标题: Efficient Multi-Scale Attention Module with Cross-Spatial Learning 作者: Daliang Ouyang, Su He, Guozhong Zhang, Mingzhu Luo, Huaiyong Guo, Jian Zhan, Zhijie Huang 论文链接: https://arxiv.org/pdf/2305.13563v2 GitHub链接: https://github.co…...

Pwn 入门核心工具和命令大全

一、调试工具&#xff08;GDB 及其插件&#xff09; GDB 启动调试&#xff1a;gdb ./binary 运行程序&#xff1a;run 或 r 设置断点&#xff1a;break *0x地址 或 b 函数名 查看寄存器&#xff1a;info registers 查看内存&#xff1a;x/10wx 0x地址 &#xff08;查看 10 个 …...

探索AI(chatgpt、文心一言、kimi等)提示词的奥秘

大家好&#xff0c;我是老六哥&#xff0c;我正在共享使用AI提高工作效率的技巧。欢迎关注我&#xff0c;共同提高使用AI的技能&#xff0c;让AI成功你的个人助理。 "AI提示词究竟是什么&#xff1f;" 这是许多初学者在接触AI时的共同疑问。 "我阅读了大量关于…...

利用飞书机器人进行 - ArXiv自动化检索推荐

相关作者的Github仓库 ArXivToday-Lark 使用教程 Step1 新建机器人 根据飞书官方机器人使用手册&#xff0c;新建自定义机器人&#xff0c;并记录好webhook地址&#xff0c;后续将在配置文件中更新该地址。 可以先完成到后续步骤之前&#xff0c;后续的步骤与安全相关&…...

小白爬虫冒险之反“反爬”:无限debugger、禁用开发者工具、干扰控制台...(持续更新)

背景浅谈 小白踏足JS逆向领域也有一年了&#xff0c;对于逆向这个需求呢主要要求就是让我们去破解**“反爬机制”**&#xff0c;即反“反爬”&#xff0c;脚本处理层面一般都是decipher网站对request设置的cipher&#xff0c;比如破解一个DES/AES加密拿到key。这篇文章先不去谈…...

Ubuntu中MySQL安装-02

服务器端安装 安装服务器端&#xff1a;在终端中输入如下命令&#xff0c;回车后&#xff0c;然后按照提示输入 sudo apt-get install mysql-server 当前使用的ubuntu镜像中已经安装好了mysql服务器端&#xff0c;无需再安装&#xff0c;并且设置成了开机自启动服务器用于接…...

大数据相关职位介绍之一(数据分析,数据开发,数据产品经理,数据运营)

大数据相关职位介绍之一 随着大数据、人工智能&#xff08;AI&#xff09;和机器学习的快速发展&#xff0c;数据分析与管理已经成为各行各业的重要组成部分。从互联网公司到传统行业的数字转型&#xff0c;数据相关职位在中国日益成为推动企业创新和提升竞争力的关键力量。以…...

使用DeepSeek API生成Markdown文件

DeepSeek技术应用与代码实现 一、DeepSeek简介 DeepSeek是一款强大的人工智能写作助手&#xff0c;能够根据用户输入的提示&#xff08;Prompt&#xff09;快速生成高质量的文章。它不仅支持批量生成文章&#xff0c;还能通过智能分段、Markdown转HTML等功能优化内容。此外&…...

java多线程学习笔记

文章目录 关键词1.什么是多线程以及使用场景?2.并发与并行3.多线程实现3.1继承 Thread 类实现3.2Runnable 接口方式实现3.3Callable接口/Future接口实现3.4三种方式总结 4.常见的成员方法&#xff08;重点记忆&#xff09;94.1setName/currentThread/sleep要点4.2线程的优先级…...

Manticore Search,新一代搜索引擎之王

吊打ES&#xff0c;新一代搜索引擎之王 概述 Manticore Search 是一个开源的分布式搜索引擎&#xff0c;专注于高性能和低延迟的搜索场景。 它基于 Sphinx 搜索引擎开发&#xff0c;继承了 Sphinx 的高效索引和查询能力&#xff0c;并在分布式架构、实时搜索、易用性等方面进…...

【MySQL】数据类型与表约束

目录 数据类型分类 数值类型 tinyint类型 bit类型 小数类型 字符串类型 日期和时间类型 enum和set 表的约束 空属性 默认值 列描述 zerofill 主键 自增长 唯一键 外键 数据类型分类 数值类型 tinyint类型 MySQL中&#xff0c;整形可以是有符号和无符号的&…...

CAG技术:提升LLM响应速度与质量

标题&#xff1a;CAG技术&#xff1a;提升LLM响应速度与质量 文章信息摘要&#xff1a; CAG&#xff08;Cache-Augmented Generation&#xff09;通过预加载相关知识到LLM的扩展上下文中&#xff0c;显著减少了检索延迟和错误&#xff0c;从而提升了响应速度和质量。与传统的R…...

上位机知识篇---Linux源码编译安装链接命令

文章目录 前言第一部分&#xff1a;Linux源码编译安装1. 安装编译工具2. 下载源代码3. 解压源代码4. 配置5. 编译6. 测试&#xff08;可选&#xff09;7. 安装8. 清理&#xff08;可选&#xff09;9.注意事项 第二部分&#xff1a;链接命令硬链接&#xff08;Hard Link&#xf…...

科研绘图系列:R语言绘制线性回归连线图(line chart)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据数据预处理画图保存图片系统信息参考介绍 科研绘图系列:R语言绘制线性回归连线图(line chart) 加载R包 library(tidyverse) library(ggthemes) libra…...

将ollama迁移到其他盘(eg:F盘)

文章目录 1.迁移ollama的安装目录2.修改环境变量3.验证 背景&#xff1a;在windows操作系统中进行操作 相关阅读 &#xff1a;本地部署deepseek模型步骤 1.迁移ollama的安装目录 因为ollama默认安装在C盘&#xff0c;所以只能安装好之后再进行手动迁移位置。 # 1.迁移Ollama可…...

Oracle 创建用户和表空间

Oracle 创建用户和表空间 使用sys 账户登录 建立临时表空间 --建立临时表空间 CREATE TEMPORARY TABLESPACE TEMP_POS --创建名为TEMP_POS的临时表空间 TEMPFILE /oracle/oradata/POS/TEMP_POS.DBF -- 临时文件 SIZE 50M -- 其初始大小为50M AUTOEXTEND ON -- 支持…...

cursor ide配置远程ssh qt c++开发环境过程记录

cursor是啥就不介绍了&#xff0c;好像是目前最好用的ai ide&#xff0c;下面主要是配置远程ssh连接linux机器进行qt5 c程序运行的配置过程记录。 一、c_cpp_properties.json 在项目根目录的.vscode目录里面新建c_cpp_properties.json文件&#xff0c;根据你的实际情况配置该文…...

yolov5错误更改与相关参数详解(train.py)

1.错误更改 main中相关参数 if __name__ __main__:parser argparse.ArgumentParser()parser.add_argument(--weights, typestr, default, helpinitial weights path)parser.add_argument(--cfg, typestr, defaultmodels/yolov5s.yaml, helpmodel.yaml path)parser.add_arg…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...