【C++】哈希表模拟:开散列技术与哈希冲突处理

在这里插入图片描述

C++语法相关知识点可以通过点击以下链接进行学习一起加油!
命名空间缺省参数与函数重载C++相关特性类和对象-上篇类和对象-中篇
类和对象-下篇日期类C/C++内存管理模板初阶String使用
String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与QueuePriority Queue与仿函数
模板进阶-模板特化面向对象三大特性-继承机制面向对象三大特性-多态机制STL 树形结构容器二叉搜索树
AVL树红黑树红黑树封装map/set哈希-开篇闭散列-模拟实现哈希

在上篇中,我们使用闭散列技术解决了哈希冲突并实现了哈希表。然而,我们发现闭散列并不理想,因此本篇将探讨如何通过开散列方法来处理哈希冲突。

请添加图片描述
Alt
🌈个人主页:是店小二呀
🌈C语言专栏:C语言
🌈C++专栏: C++
🌈初阶数据结构专栏: 初阶数据结构
🌈高阶数据结构专栏: 高阶数据结构
🌈Linux专栏: Linux

🌈喜欢的诗句:无人扶我青云志 我自踏雪至山巅 请添加图片描述

文章目录

  • 一、开散列(哈希桶)
  • 二、实现哈希表
    • 2.1 哈希表基本构架
    • 2.2 哈希表插入数据
    • 2.3 哈希表析构函数
    • 2.4 哈希表扩容
    • 2.5 哈希表删除数据
    • 2.6 哈希桶中查找
  • 三、HashTable.h

一、开散列(哈希桶)

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

从上图可以看出,开散列中每个桶中放的都是发生哈希冲突的元素

二、实现哈希表

2.1 哈希表基本构架

template<class K>struct HashFunc{size_t operator()(const K& key){return (size_t)key;}};template<>
struct HashFunc<string>
{size_t operator()(const string& key){size_t hash = 0;for (auto ch : key){hash *= 131;hash += ch;}return hash;}
};
namespace hash_buck
{template<class K,class V>struct HashNode{pair<K, V> _kv;HashNode<K, V> _next;};template<class K, class V,class Hash = HashFunc<K>>class HashTable{public:typedef HashNode<K, V> Node;HashTable(){_tables.resize(10nullptr);}private:vector<Node*> _tables;//vector<list<..>> _tables;size_t _n = 0;};
}

这里如果想实现哈希桶结构,可以使用一个个节点连接起来或者直接套上一个链表容器。这里为了更好地实现迭代器,我们选择第一种方式,第二种方式实现迭代器有一丝麻烦。

开散列中每个桶中放的都是发生哈希冲突的元素 ,并没有顺序之分。

2.2 哈希表插入数据

在这里插入图片描述

bool Insert(const pair<K, V>& kv)
{Hash hs;size_t hashi = hs(kv.first) % _tables.size();//进行头插操作Node* newnode = new Node(kv);_tables[hashi]->_next = newnode;_tables[hashi] = newnode;
}

这里链表节点只需要单纯地存储数据,那么只需要设计单链表即可,没有必要设双向链表。对于单链表插入数据,一般推荐采用头插,由于尾插需要找尾比较麻烦。

2.3 哈希表析构函数

这里涉及堆上空间资源的开辟,一般需要涉及析构函数进行资源处理。

由于vector容器元素为内置类型,析构函数对内置类型不进行处理,那么就导致指向空间没有得到释放,需要显式写析构函数,完成资源释放。

~HashTable()
{for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}}
}

2.4 哈希表扩容

由于哈希桶去解决哈希冲突对于哈希表空间需要相对比较小,不同于开发定址法去解决哈希冲突占用表内空间,而是表中一个指针指向一个空间。

负载因子满足1即可扩容】:_n == _tables.size()

第一个方案】:复用Insert完成CV操作

//扩容逻辑if (_n == _tables.size()){//这里可以忽略类型HashTable NewTable;NewTable._tables.resize(n * 10);//CV工作for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){NewTable.Insert(_tables[i]._kv);cur = cur->_next;}}_tables.swap(NewTable);}

缺陷】:如果存在一万个节点,意味着需要复制一万个节点又要释放一万个节点,显得很浪费

第二个方案】:直接将节点拿下来

在这里插入图片描述

if (_n == _tables.size())
{vector<Node*> NewTable(n * 10, nullptr);for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;cur->_next = _tables[i];_tables[i] = cur;cur = cur->_next;}_tables[i] = nullptr;}_tables.swap(NewTable);
}

关于这段代码是比较难懂,每个指针指向一块空间,先cur->next = newTables[hashi]抓住新表的位置,newTables[hashi] = cur新表将旧表节点拿下来,可以好好理解拿下来的逻辑,得到了这个地址,这个地址指向一块节点。

这里会导致newTables[hashi]_tables[i]共同指向一块空间,虽然不会去使用旧表去影响到新表,为了保险起见,可以将旧表这块位置指针置空。

2.5 哈希表删除数据

bool Erase(const K& key)
{Hash hs;size_t hashi = hs(kv.first) % _tables.size();Node* cur = _tables[hashi];Node* prev = nullptr;while (cur){if (cur->_kv.first == key){if (prev == nullptr){_tables[hashi] = cur->_next;}else{prev->_next = cur->_next;}delete cur;return true;}else{prev = cur;cur = cur->_next;}}return false;
}

这里删除数据,无非需要考虑两种情况的删除,一种是删除第一个节点,另一种是删除其他节点prev->_next = cur->_next。在删除节点需要前后兼顾,保存下前驱指针指向节点。

2.6 哈希桶中查找

Node* Find(const K& key)
{Hash hs;size_t hashi = hs(kv.first) % _tables.size();Node* cur = _tables[hashi];while (cur){if (cur->_kv.first == key){return &cur->_kv.first;}else{cur = cur->_next;}}return nullptr;
}

三、HashTable.h

template<class K>struct HashFunc{size_t operator()(const K& key){return (size_t)key;}};template<>
struct HashFunc<string>
{size_t operator()(const string& key){size_t hash = 0;for (auto ch : key){hash *= 131;hash += ch;}return hash;}
};namespace hash_bucket
{template<class K,class V>struct HashNode{pair<K, V> _kv;HashNode<K, V> _next;};template<class K, class V,class Hash = HashFunc<K>>class HashTable{public:typedef HashNode<K, V> Node;HashTable(){_tables.resize(10,nullptr);}~HashTable(){for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}}}bool Insert(const pair<K, V>& kv){Hash hs;size_t hashi = hs(kv.first) % _tables.size();//扩容逻辑//if (_n == _tables.size())//{//	//这里可以忽略类型//	HashTable NewTable;//	NewTable._tables.resize(n * 10);//	//	//CV工作//	for (size_t i = 0; i < _tables.size(); i++)//	{//		Node* cur = _tables[i];//		while (cur)//		{//			NewTable.Insert(_tables[i]._kv);//			cur = cur->_next;//		}//	}//	_tables.swap(NewTable);//}if (_n == _tables.size()){vector<Node*> NewTable(n * 10, nullptr);for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;// 头插新表的位置size_t hashi = hs(kot(cur->_data)) % NewTable.size();cur->_next = NewTable[hashi];NewTable[hashi] = cur;cur = cur->_next;}_tables[i] = nullptr;}_tables.swap(NewTable);}//进行头插操作Node* newnode = new Node(kv);_tables[hashi]->_next = newnode;_tables[hashi] = newnode;++_n;}Node* Find(const K& key){Hash hs;size_t hashi = hs(kv.first) % _tables.size();Node* cur = _tables[hashi];while (cur){if (cur->_kv.first == key){return &cur->_kv.first;}else{cur = cur->_next;}}return nullptr;}bool Erase(const K& key){Hash hs;size_t hashi = hs(kv.first) % _tables.size();Node* cur = _tables[hashi];Node* prev = nullptr;while (cur){if (cur->_kv.first == key){if (prev == nullptr){_tables[hashi] = cur->_next;}else{prev->_next = cur->_next;}delete cur;return true;}else{prev = cur;cur = cur->_next;}}return false;}private:vector<Node*> _tables;size_t _n = 0;};
}

在这里插入图片描述

以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二呀C++笔记,希望对你在学习C++语言旅途中有所帮助!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

「Mac畅玩鸿蒙与硬件18」鸿蒙UI组件篇8 - 高级动画效果与缓动控制

高级动画可以显著提升用户体验&#xff0c;为应用界面带来更流畅的视觉效果。本篇将深入介绍鸿蒙框架的高级动画&#xff0c;包括弹性动画、透明度渐变和旋转缩放组合动画等示例。 关键词 高级动画弹性缓动自动动画缓动曲线 一、Animation 组件的高级缓动曲线 缓动曲线&#…

SpringBoot源码解析(二):启动流程之引导上下文DefaultBootstrapContext

SpringBoot源码系列文章 SpringBoot源码解析(一)&#xff1a;启动流程之SpringApplication构造方法 SpringBoot源码解析(二)&#xff1a;启动流程之引导上下文DefaultBootstrapContext 目录 前言一、入口二、DefaultBootstrapContext1、BootstrapRegistry接口2、BootstrapCon…

ELK之路第三步——日志收集筛选logstash和filebeat

logstash和filebeat&#xff08;偷懒版&#xff09; 前言logstash1.下载2.修改配置文件3.测试启动4.文件启动 filebeat1.下载2.配置3.启动 前言 上一篇&#xff0c;我们说到了可视化界面Kibana的安装&#xff0c;这一篇&#xff0c;会简单介绍logstash和filebeat的安装和配置。…

Python毕业设计选题:基于Hadoop的租房数据分析系统的设计与实现

开发语言&#xff1a;Python框架&#xff1a;flaskPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 系统首页 房屋信息详情 个人中心 管理员登录界面 管理员功能界面 用户管理界面 房屋信…

深度学习笔记之BERT(一)BERT的基本认识

深度学习笔记之BERT——BERT的基本认识 引言回顾&#xff1a;Transformer的策略回顾&#xff1a;Word2vec的策略和局限性 BERT \text{BERT} BERT的基本理念抽象的双向BERT的预训练策略 预训练与微调 引言 从本节开始&#xff0c;将介绍 BERT \text{BERT} BERT系列模型以及其常…

YOLOv8改进,YOLOv8引入ResCBAM注意力机制,二次创新C2f结构

摘要 腕部创伤甚至骨折在日常生活中经常发生,在儿童中,他们占骨折病例的很大比例。在进行手术之前,外科医生通常会要求患者先进行 X 光成像,并根据放射科医生的分析进行手术准备。随着神经网络的发展,“You Only Look Once”(YOLO)系列模型在骨折检测中的应用越来越广泛…

CSS--两列网页布局,三列布局和多行多列布局

两列网页布局 两列网页布局实验 先将一个未运用浮动效果的网页结构写出来 <style>header{/* 给页眉设置宽高和样式 */width:1000px;height: 40px;background-color: gray;border: 3px brown solid;margin-bottom: 5px;}article{width:1000px;height: 600px;background-c…

如何使用Web-Check和cpolar实现安全的远程网站监测与管理

文章目录 前言1.关于Web-Check2.功能特点3.安装Docker4.创建并启动Web-Check容器5.本地访问测试6.公网远程访问本地Web-Check7.内网穿透工具安装8.创建远程连接公网地址9.使用固定公网地址远程访问 前言 本期给大家分享一个网站检测工具Web-Check&#xff0c;能帮你全面了解网…

Webserver(2.6)信号

目录 信号的概念信号相关的函数killraiseabortalarm1s钟电脑能数多少个数&#xff1f; setitimer过3s以后&#xff0c;每隔2s定时一次 信号捕捉函数signalsigaction 信号集sigprocmask编写一个程序&#xff0c;把所有的常规信号未决状态打印到屏幕 sigchld信号 信号的概念 比如…

AprilTag在相机标定中的应用简介

1. AprilTag简介 相机标定用的标靶类型多样,常见的形式有棋盘格标靶和圆形标靶。今天要介绍的AprilTag比较特别,它是一种编码形式的标靶。其官网为AprilTag,它是一套视觉基准系统,包含标靶编解码方法(Tag生成)和检测算法(Tag检测),可用于AR、机器人、相机标定等领域。…

Qt报错QOCI driver not loaded且QOCI available的解决方法

参考 Linux Qt 6安装Oracle QOCI SQL Driver插件&#xff08;适用WSL&#xff09; 安装 QOCI 插件完成后运行 Qt 项目报错&#xff1a; qt.sql.qsqldatabase: QSqlDatabase: QOCI driver not loaded qt.sql.qsqldatabase: QSqlDatabase: available drivers: QMIMER QPSQL QODBC…

【MySQL】 穿透学习数据库理论与知识剖析

前言&#xff1a;本节内容讲述一些数据库的基本概念。 第一个部分就是数据库相关的概念&#xff0c; 比如什么是数据库&#xff0c; 如何理解mysqld以及mysql。第二部分理解数据库和表在系统层面的形式。 第三部分就是mysql的一些操作分类。 第四部分就是数据库的插件配置这些。…

Web Broker(Web服务应用程序)入门教程(1)

1、介绍 Web Broker 组件&#xff08;位于工具面板的“Internet”选项卡中&#xff09;可以帮助您创建与特定统一资源标识符&#xff08;URI&#xff09;相关联的事件处理程序。当处理完成后&#xff0c;您可以通过编程方式构建 HTML 或 XML 文档&#xff0c;并将它们传输给客…

网络安全法详细介绍——爬虫教程

目录 [TOC](目录)一、网络安全法详细介绍1. 网络安全法的主要条款与作用2. 网络安全法与爬虫的关系3. 合法使用爬虫的指南 二、爬虫的详细教程1. 准备环境与安装工具2. 使用requests库发送请求3. 解析HTML内容4. 使用robots.txt规范爬虫行为5. 设置请求间隔6. 数据清洗与存储 三…

25国考照片处理器使用流程图解❗

1、打开“国家公务员局”网站&#xff0c;进入2025公务员专题&#xff0c;找到考生考务入口 2、点击下载地址 3、这几个下载链接都可以 4、下载压缩包 5、解压后先看“使用说明”&#xff0c;再找到“照片处理工具”双击。 6、双击后会进入这样的界面&#xff0c;点击&…

Go 语言之搭建通用 Web 项目开发脚手架

Go 语言之搭建通用 Web 项目开发脚手架 MVC 模式 MVC 模式代表 Model-View-Controller&#xff08;模型-视图-控制器&#xff09; 模式。这种模式用于应用程序的分层开发。 Model&#xff08;模型&#xff09; - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑&…

江协科技STM32学习- P34 I2C通信外设

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

windows在两台机器上测试 MySQL 集群实现实时备份

在两台机器上测试 MySQL 集群实现实时备份的基本步骤&#xff1a; 一、环境准备 机器配置 确保两台机器&#xff08;假设为服务器 A 和服务器 B&#xff09;能够互相通信&#xff0c;例如它们在同一个局域网内&#xff0c;并且开放了 MySQL 通信所需的端口&#xff08;默认是 …

【MIT-OS6.S081笔记1】xv6环境搭建

最近开始做一个操作系统的神课MIT-OS6.S081&#xff0c;我做的是老版本的2020版本的&#xff0c;环境使用的是VirtualBox的Ubuntu系统&#xff0c;在这里记录一下学习的过程。首先需要搭建一下环境&#xff0c;参考官网Tools Used in 6.S081&#xff0c;这个知乎文章也写得很好…

深度学习基础—语言模型和序列生成

引言 深度学习基础—循环神经网络&#xff08;RNN&#xff09;https://blog.csdn.net/sniper_fandc/article/details/143417972?fromshareblogdetail&sharetypeblogdetail&sharerId143417972&sharereferPC&sharesourcesniper_fandc&sharefromfrom_link 上…