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

数据结构(15)——哈希表(2)

欢迎来到博主的专栏:数据结构
博主ID:代码小豪

文章目录

    • 开散列式哈希表
      • 开散列式哈希表的结构
      • insert
    • find和erase
      • 析构函数

开散列式哈希表

前文提到,解决哈希冲突的方法分为闭散列和开散列,由于开散列的底层原理与闭散列的哈希表的底层原理不同,因此博主重新开一篇文章讲解。

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

这样说好像有点抽象了,简单来说就是哈希表中存的不再是元素,而是一个指向链表的指针,这个链表存的是具有相同映射地址的key值。若某个地址不存在任何元素,则记为nullptr。
在这里插入图片描述

这种将多个相同映射值的数据装在一个集合的哈希结构也叫做哈希桶,即哈希表的每个元素都是一个桶,如果桶不存在任何元素,也就是nullptr。

开散列式哈希表的结构

关于哈希表的底层,博主仍然采用vector作为底层,根据上述的内容,该vector容器应该存指向链表头结点的指针,而节点应该存一个数据,还有指向下一个节点的指针。

节点的结构如下:

template<class T>
struct hash_Node
{hash_Node(const T& data):_data(data),_next(nullptr){}T _data;hash_Node<T>* _next;
};

哈希表的结构如下:

template<class key,class value>
class hash_tab
{
public:typedef pair<key, value> value_type;typedef hash_Node<value_type> Node;hash_tab(){_tab.resize(10);}
private:vector<Node<value_type>*> _tab;//哈希表size_t _n;//有效数据个数
};

insert

先使用哈希函数求出待插入元素的映射值,然后将节点头插在该位置。

插入方法如下:
(1)求出映射值
(2)在地址处将节点头插在对应的链表处
在这里插入图片描述
可以发现,开散列式的哈希表不需要线性探测,即使发生了哈希冲突,插入的效率依然是O(1)。插入的效率非常高。但是查找的效率却并非如此,比如在上例中查找34,需要遍历链表,因此插入的效率依然与负载因子挂钩,

负载因子=有效数据个数%哈希表的可容纳地址总数

由于开散列式哈希表空间利用率远高于闭散列,因此通常负载因子会控制在1以上。博主采用1作为负载因子的限制值。当负载因子超过1时,就要对哈希表进行扩容。

开散列式的哈希表依然采用异地扩容的战略,理由与闭散列式的哈希表一致:
哈希表扩容之后,key值的映射值也随之改变,因此需要重新插入。

异地扩容的方法如下:
(1)建立新表
(2)将旧表的内容移动到新表当中
(4)将旧表的所有指针置为nullptr
(3)交换新旧两表
在这里插入图片描述

bool insert(const value_type& val)
{if (_n / _tab.size() >= 1)//如果负载因子>=1就要扩容{hash_tab<key, value> newtab;newtab._tab.resize(2 * _tab.size());//2倍扩容for (int i = 0; i < _tab.size(); i++)//遍历旧表{Node* cur = _tab[i];while (cur != nullptr){Node* next = cur->_next;newtab.insert(cur);}_tab[i] = nullptr;}_tab.swap(newtab._tab);//交换新旧两表}size_t hashnum = val.first % _tab.size();Node* newnode = new Node(val);newnode->_next = _tab[hashnum];_tab[hashnum] = newnode;_n++;
}
privatebool insert(Node* newnode)//重载了一个插入节点的版本
{size_t hashnum = newnode->_data.first % _tab.size();newnode->_next = _tab[hashnum];_tab[hashnum] = newnode;_n++;return true;
}

为了支持插入节点,博主重载了一个insert函数,这个函数由于只用于将旧表的节点转移到新表,因此将接口隐藏起来。

find和erase

find函数的原理如下:

通过计算出key值的映射位置,在映射的桶中查找对应key值的数据,返回该节点。

Node* find(const key& key)
{size_t hashnum = key % _tab.size();Node* cur = _tab[hashnum];while (cur != nullptr){if (cur->_data.first == key){return cur;}cur = cur->_next;}return nullptr;
}

erase函数的原理如下:

通过find函数找到待删除的节点,然后删除该节点,删除节点的操作和链表删除节点的操作一致,博主不多赘述。

析构函数

由于开散列式哈希表的节点是开辟在堆区上的(new出来的),因此需要对这些数据进行销毁,否则就会导致内存泄漏。因此我们需要为哈希表设计一个析构函数。

析构函数的思路如下:
(1)遍历整个表的哈希桶
(2)将每个哈希桶内的数据释放
(3)释放后将哈希桶置为空桶。

~hash_tab()
{for (size_t i=0;i<_tab.size();i++){Node* cur = _tab[i];while (cur != nullptr){Node* next = cur->_next;delete cur;cur = next;}_tab[i] = nullptr;_n = 0;}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • C#从入门到精通(22)—Path类的使用
  • 2024 高教社杯 数学建模国赛 (C题)深度剖析|农作物的种植策略|数学建模完整代码+建模过程全解全析
  • 【项目一】基于pytest的自动化测试框架day1
  • CRE6959AM70V055S 超低待机功耗反激式开关电源芯片
  • CSS解析:盒模型
  • linux~~目录结构远程登录教程(xshell+xftp)
  • 鼠标控制dom元素的大小。采用ResizeObserver——监听元素大小的变化
  • HarmonyOS开发实战( Beta5版)合理使用动画丢帧规范实践
  • SpringBoot+Vue实现大文件上传(断点续传-后端控制(一))
  • 卷积神经网络与小型全连接网络在MNIST数据集上的对比
  • 设计模式—2—单例模式
  • 基于 XILINX FPGA 的 Cameralink Full 模式相机采集系统技术实施方案研究报告
  • WebRTC协议下的视频汇聚融合技术:EasyCVR视频技术构建高效视频交互体验
  • 计算机网络(八股文)
  • 微信小程序rpx和px关系
  • [Vue CLI 3] 配置解析之 css.extract
  • [译] 怎样写一个基础的编译器
  • [译]Python中的类属性与实例属性的区别
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • 【知识碎片】第三方登录弹窗效果
  • Debian下无root权限使用Python访问Oracle
  • Docker下部署自己的LNMP工作环境
  • jquery ajax学习笔记
  • Map集合、散列表、红黑树介绍
  • MySQL的数据类型
  • Python利用正则抓取网页内容保存到本地
  • React16时代,该用什么姿势写 React ?
  • React-生命周期杂记
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • Wamp集成环境 添加PHP的新版本
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 精彩代码 vue.js
  • 译自由幺半群
  • 栈实现走出迷宫(C++)
  • python最赚钱的4个方向,你最心动的是哪个?
  • ​ 轻量应用服务器:亚马逊云科技打造全球领先的云计算解决方案
  • ​必胜客礼品卡回收多少钱,回收平台哪家好
  • ​数据链路层——流量控制可靠传输机制 ​
  • !!Dom4j 学习笔记
  • # 执行时间 统计mysql_一文说尽 MySQL 优化原理
  • (145)光线追踪距离场柔和阴影
  • (9)STL算法之逆转旋转
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (八十八)VFL语言初步 - 实现布局
  • (接口封装)
  • (七)c52学习之旅-中断
  • (四)【Jmeter】 JMeter的界面布局与组件概述
  • (一)基于IDEA的JAVA基础10
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • (转)3D模板阴影原理
  • (转)四层和七层负载均衡的区别
  • (转)为C# Windows服务添加安装程序