一文看懂哈希表并学会使用C++ STL 中的哈希表

2023-10-29

    最近在刷题以及做编程练习的作业时经常会用到哈希表,碰到一些想用的函数时每次都看别人的博客,现结合别人的博客对哈希表做个总结。

1. 哈希表的定义

(1)哈希表的作用
    哈希表就是在关键字和存储位置之间建立对应关系,使得元素的查找可以以O(1)的效率进行, 其中关键字和存储位置之间是通过散列函数建立关系,记为: L o c ( i ) = H a s h ( k e y i ) 。 Loc(i) = Hash(key_i)。 Loc(i)=Hash(keyi)
关键字和存储地址之间的对应关系
(2) 常见的散列函数
    1)线性定址法:直接取关键字的某个线性函数作为存储地址,散列函数为: H a s h ( k e y ) = a × k e y + b Hash(key) = a × key + b Hash(key)=a×key+b
    2)除留余数法:将关键字对某一小于散列表长度的数p取余的结果作为存储地址,散列函数为: H a s h ( k e y ) = k e y    m o d    p Hash(key) = key \;mod \; p Hash(key)=keymodp
    3)平方取中法:对关键字取平方,然后将得到结果的中间几位作为存储地址;
    4)折叠法:将关键字分割为几部分,然后将这几部分的叠加和作为存储地址。

(3) 地址冲突解决方法
    通过以上方法构建的哈希表在理想的情况下能够做到一个关键字对应一个地址,但是实际情况是会有冲突发生,也就是散列函数会将多个关键字映射到同一个地址上。以下是一些解决冲突的方法:
  1)开放地址法:
    ①线性探测法:当发生冲突时,就顺序查看下一个存储位置,如果位置为空闲状态,就将该关键字存储在该位置上,如果还是发生冲突,就依次往后查看,当查看到存储空间的末尾时还是找不到空位置,就返回从头开始查看;
在这里插入图片描述
    ②平方探测法:不同于前面线性探测法依次顺序查看下一个位置是否能存储元素,平方探测的规则是以 1 2 , − 1 2 , 2 2 , − 2 2 , . . . , 1^2, -1^2, 2^2, -2^2,..., 12,12,22,22,...探测新的存储位置能否存储元素;
    ③再散列法:利用两个散列函数,当通过第一个散列函数得到关键字的存储地址发生冲突时,再利用第二个散列函数计算出地址增量,地址计算方式如下:
H i = ( H a s h 1 ( k e y ) + i ∗ H a s h 2 ( k e y ) ) % p H_i = (Hash_1(key)+i*Hash_2(key))\%p Hi=(Hash1(key)+iHash2(key))%p
    ④伪随机数法: 当发生地址冲突时,加入一个随机数作为地址增量寻找新的存储地址,地址计算方式如下:
H i = ( H a s h ( k e y ) + d i ) % p ,    其 中 d i 为 随 机 数 H_i = (Hash(key)+d_i)\%p, \;其中d_i为随机数 Hi=(Hash(key)+di)%p,di
  2)拉链法
    将具有相同存储地址的关键字链成一单链表, m个存储地址就设m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构,假设散列函数为 Hash(key) = key %13,其拉链存储结构为:

在这里插入图片描述

2. 如何使用STL库中的哈希表

(1)导入头文件
  #include<unordered_map>
(2)哈希表的声明和初始化
    1)声明

unordered_map<elemType_1, elemType_2> var_name; //声明一个没有任何元素的哈希表,
//其中elemType_1和elemType_2是模板允许定义的类型,如要定义一个键值对都为Int的哈希表:
unordered_map<int, int> map;

    2)初始化
    以上在声明哈希表的时候并没有给unordered_map传递任何参数,因此调用的是unordered_map的默认构造函数,生成一个空容器。初始化主要有一下几种方式:
     a)在定义哈希表的时候通过初始化列表中的元素初始化:

unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
//如果知道要创建的哈希表的元素个数时,也可以在初始化列表中指定元素个数
unordered_map<int, int> hmap{ {{1,10},{2,12},{3,13}},3 };

     b)通过下标运算来添加元素:

//当我们想向哈希表中添加元素时也可以直接通过下标运算符添加元素,格式为: mapName[key]=value;
//如:hmap[4] = 14;
//但是这样的添加元素的方式会产生覆盖的问题,也就是当hmap中key为4的存储位置有值时,
//再用hmap[4]=value添加元素,会将原哈希表中key为4存储的元素覆盖
hmap[4] = 14;
hmap[5] = 15;
cout << hmap[4];  //结果为15

     c)通过insert()函数来添加元素:

//通过insert()函数来添加元素的结果和通过下标来添加元素的结果一样,不同的是insert()可以避免覆盖问题,
//insert()函数在同一个key中插入两次,第二次插入会失败
hmap.insert({ 5,15 });
hmap.insert({ 5,16 });
cout << hmap[5];  //结果为15

     d)复制构造,通过其他已初始化的哈希表来初始新的表:

unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
unordered_map<int, int> hmap1(hmap);

3. STL中哈希表的常用函数

(1) begin( )函数:该函数返回一个指向哈希表开始位置的迭代器

unordered_map<int, int>::iterator iter = hmap.begin(); //申请迭代器,并初始化为哈希表的起始位置
cout << iter->first << ":" << iter->second;

(2) end( )函数:作用于begin函数相同,返回一个指向哈希表结尾位置的下一个元素的迭代器

unordered_map<int, int>::iterator iter = hmap.end();

(3) cbegin() 和 cend():这两个函数的功能和begin()与end()的功能相同,唯一的区别是cbegin()和cend()是面向不可变的哈希表

const unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
unordered_map<int, int>::const_iterator iter_b = hmap.cbegin(); //注意这里的迭代器也要是不可变的const_iterator迭代器
unordered_map<int, int>::const_iterator iter_e = hmap.cend();

(4) empty()函数:判断哈希表是否为空,空则返回true,非空返回false

bool isEmpty = hmap.empty();

(5) size()函数:返回哈希表的大小

int size = hmap.size();

(6) erase()函数: 删除某个位置的元素,或者删除某个位置开始到某个位置结束这一范围内的元素, 或者传入key值删除键值对

unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
unordered_map<int, int>::iterator iter_begin = hmap.begin();
unordered_map<int, int>::iterator iter_end = hmap.end();
hmap.erase(iter_begin);  //删除开始位置的元素
hmap.erase(iter_begin, iter_end); //删除开始位置和结束位置之间的元素
hmap.erase(3); //删除key==3的键值对

(7) at()函数:根据key查找哈希表中的元素

unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
int elem = hmap.at(3);

(8) clear()函数:清空哈希表中的元素

hmap.clear()
(9) find()函数:以key作为参数寻找哈希表中的元素,如果哈希表中存在该key值则返回该位置上的迭代器,否则返回哈希表最后一个元素下一位置上的迭代器
unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
unordered_map<int, int>::iterator iter;
iter = hmap.find(2); //返回key==2的迭代器,可以通过iter->second访问该key对应的元素
if(iter != hmap.end())  cout << iter->second;

(10) bucket()函数:以key寻找哈希表中该元素的储存的bucket编号(unordered_map的源码是基于拉链式的哈希表,所以是通过一个个bucket存储元素)

int pos = hmap.bucket(key);

(11) bucket_count()函数:该函数返回哈希表中存在的存储桶总数(一个存储桶可以用来存放多个元素,也可以不存放元素,并且bucket的个数大于等于元素个数)

int count = hmap.bucket_count();

(12) count()函数: 统计某个key值对应的元素个数, 因为unordered_map不允许重复元素,所以返回值为0或1

int count = hmap.count(key);

(13) 哈希表的遍历: 通过迭代器遍历

unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
unordered_map<int, int>::iterator iter = hmap.begin();
for( ; iter != hmap.end(); iter++){
 cout << "key: " <<  iter->first  << "value: " <<  iter->second <<endl;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一文看懂哈希表并学会使用C++ STL 中的哈希表 的相关文章

随机推荐

  • 详细分析vcoco2014HOI数据集

    vcoco images 图片 train2014 共82783张 COCO train2014 000000581921 jpg COCO train2014 000000581922 jpg COCO train2014 0000005
  • 记录ubuntu启动卡在logo界面有鼠标进不了桌面的经历,以及安装ubuntu踩的坑

    出现问题前 我之前安装过很多次ubuntu 不管是虚拟机 4 5次 还是双系统 3 4次 每次都是我自己搞崩的 就是我和之前一样开始安装搜狗输入法 之前没出过问题 然后就是这次安装完 我感觉和之前不一样 就是之前不知道为什么安装完会有pin
  • 波兰表达式 - 前,中,后缀表达式计算转换

    先看一个算术题 3 4 5 6 29 前缀表达式 3456 中缀表达式 3 4 5 6 你会算的 后缀表达式 34 5 6 利用栈的特性来运算表达式 当前我只拿到了 3 4 5 6 让我求它的前缀和后缀 求后缀口诀 1 从左到右看 数字忙显
  • ubuntu 提示 Could not get lock /var/lib/dpkg/lock-frontend.的处理办法

    今天可能操作删除某个程序的时候提示无法删除 给锁定了 一直显示 Waiting for cache lock Could not get lock var lib dpkg lock frontend It is held by proce
  • Optimizer trance—mysql进阶(五十三)

    前面介绍了 如果加个format JOSN会把数据以json的格式返回 如果想看查询的额外信息 还可以在explain之后加个show warning查看 其中如果code为1003 则代表message里的内容是mysql优化器优化之后的
  • Python学习十二:Flask框架

    文章目录 一 Flask 简介 1 1 安装虚拟环境 1 1 1 安装Virtualenv 1 1 2 创建虚拟环境 1 1 3 激活虚拟环境 1 2 安装Flask 1 3 第一个Flask 二 Flask基础 2 1 开启调试模式 2
  • Java测试(1)

    1 什么是软件测试 软件测试就是软件测试人员验证软件是否满足用户的需求 测试的时候要测试满足和不满足的数据 2 软件测试和软件开发的区别 1 本身 开发 广度小 专业度高 测试 所需技能比价广泛 但是专业度低 2 软件测试和软件调式 目的
  • 阿里版GPT来袭——“通义千问”

    4月7日 阿里云在官方公众号中宣布 大模型 通义千问 开始邀请测试 你好 我叫通义千问 在 通义千问 的自我介绍中可知 它是达摩院自主研发的预训练语言模型 能够回答问题 创作文字 还能表达观点 撰写代码 基于上述能力 通义千问 认为其可以在
  • 数据仓库的选择

    author skate time 2010 03 11 数据仓库的选择 数据仓库的选择单从技术方面要从服务器硬件 数据库软件 ETL和前端展示软件 存储系统 仓库的架构设计几方面综合考虑 根据数据库的操作类型不同 数据库一般分为OLAP和
  • ORA-12505, TNS:listener does not currently know of SID given in connect descriptor解决方式

    启动项目连接oracle数据报 ORA 12505 TNS listener does not currently know of SID given in connect descriptor ORA 12505 TNS 监听程序当前无法
  • .NET网站部署到阿里云服务器经验分享

    由于笔者需要将自己的网站上线 所以第一步就是去买了一个阿里云服务器 想要远程访问的话 首先是云数据库的部署 然后是网站的部署 1 云数据库的部署 过程 在云服务器上下载SQLServer 然后把本地的数据库 架构和数据 使用脚本导出保存 再
  • 【千律】OpenCV基础:Hough圆检测

    环境 Python3 8 和 OpenCV 内容 Hough圆检测 将直角坐标系中的一个圆映射为新坐标系中的一个点 对于原直角坐标系中的每一个圆 可以对应 a b r 这样一个点 这个点即为新三维中的点 标准法实现步骤 1 获取原图像的边缘
  • 如果判断服务器是否在被CC攻击?

    什么是CC攻击 CC攻击的前身名为Fatboy攻击 是利用不断对网站发送连接请求致使形成拒绝服务的目的 攻击者通过代理服务器或者肉鸡向向受害主机不停地发大量数据包 造成对方服务器资源耗尽 一直到宕机崩溃 怎么判断是否被CC攻击 CC攻击主要
  • php怎么获取微信code,PHP tp3.2微信公众号静默授权获取code 获取openid

    PHP tp3 2微信公众号静默授权获取code 获取openid 发布时间 2018 02 24 14 46 浏览次数 1530 标签 PHP tp code openid 一 调用静默授权接口 基于thinkphp3 2的 1 获取co
  • [C语言]字符串处理 - 以指定的字符串分割字符串(支持中文字符)

    C语言 字符串处理 以指定的字符串分割字符串 支持中文字符 函数StringSplit 分割字符串到一个字符串数组中 其中该数组第0位为分割后字符串的个数 函数StringSplit Struct 以定义一个新结构的方式来实现该函数 C代码
  • 单片机----

    开启内部上拉电阻 pbph 0 1
  • C++多线程并发总结

    文章目录 1 线程创建与管理 1 1 并发与并行 1 2 多线程并发与多进程并发 2 C 线程创建 2 1 std thread 线程同步之互斥锁 std mutex std unique lock lock与unlock保护共享资源 lo
  • Java封装OkHttp3工具类

    一 准备工作 Maven项目在pom文件中引入jar包
  • 阿里云 MSE 助力开迈斯实现业务高增长背后带来的服务挑战

    开迈斯新能源科技有限公司于 2019 年 5 月 16 日成立 目前合资股东分别为大众汽车 中国 投资有限公司 中国第一汽车股份有限公司 一汽 大众汽车有限公司 增资扩股将在取得适当监督 包括反垄断 审批后完成 万帮数字能源股份有限公司和安
  • 一文看懂哈希表并学会使用C++ STL 中的哈希表

    最近在刷题以及做编程练习的作业时经常会用到哈希表 碰到一些想用的函数时每次都看别人的博客 现结合别人的博客对哈希表做个总结 本篇博客的主要内容如下 1 哈希表的定义 2 如何使用STL库中的哈希表 3 STL中哈希表的常用函数 1 哈希表的