【C++进阶学习】第十弹——哈希的原理与实现——链地址法的原理与讲解

开放地址法:【C++进阶学习】第九弹——哈希的原理与实现——开放寻址法的讲解-CSDN博客

前言:

哈希的整体思想就是建立映射关系,前面的开放地址法的讲解中,也对哈希的原理做了详细的讲解,今天就来讲解一下实现哈希的另一种主要方法——链地址法

目录

一、链地址法的基本思想

二、链地址法的实现步骤

节点结构

构造和析构

插入操作

查找操作

删除操作

打印操作

三、测试代码

四、总结


一、链地址法的基本思想

前面所讲的开放地址法,我们是通过建立一种映射的关系来存储数据

这种方法时常会遇到图中的这种情况,有利有弊

链地址法则是另一种思路:将哈希表的每个槽指向一个链表(或其他数据结构,如动态数组,红黑树等),所有哈希到同一个槽的元素都存储在这个链表中。这样,即使发生了哈希冲突,也可以通过链表来存储多个元素。

二、链地址法的实现步骤

首先,我们先来看一下链地址法的重点:

  1. 定义哈希表结构:哈希表通常包含一个数组,数组的每个元素是一个链表的头节点。
  2. 哈希函数:设计一个哈希函数,将键映射到数组的索引位置。
  3. 插入操作
    • 计算键的哈希值,得到索引位置。
    • 将键值对插入到对应索引位置的链表中。
  4. 查找操作
    • 计算键的哈希值,得到索引位置。
    • 在对应索引位置的链表中查找键值对。
  5. 删除操作
    • 计算键的哈希值,得到索引位置。
    • 在对应索引位置的链表中删除键值对。

节点结构

与开放寻址法一样,因为我们并不知道插入要操作何种类型的数据,可能是整形,浮点型或string的,所以我们可以选择将它们全转化为整形来处理,这里就需要我们借助仿函数和模板特化来实现

	template<class K>       
struct HashFunc         //仿函数,这里的功能是将其他类型转化为整形
{size_t operator()(const K& key){return (size_t)key;}
};
template<>     //特化
struct HashFunc<string>    //string类的不可以直接转化为整形,所以需要特殊处理
{size_t operator()(const string& key){size_t hash = 0;for (auto e : key){hash *= 31;hash += e;}return hash;}
};template<class K,class V>struct HashNode{HashNode* next;pair<K, V> _kv;HashNode(const pair<K,V>& kv)    //构造函数:next(nullptr)     //初始化列表,_kv(kv){}};template<class K,class V, class Hash = HashFunc<K>>class HashTable{typedef HashNode<K, V> Node;public:private://vector<list> _tables;    //这也是一种思路vector<Node*> _tables;size_t _n;};

构造和析构

因为在节点中我们使用了指针类型的数据,所以我们尽量将构造和析构函数自己定义,这里没啥难度,看代码即可:

        HashTable(){_tables.resize(10);     //初始化大小为19}~HashTable(){for (int i = 0; i < _tables.size(); i++){Node* cur = _tables[i];     //每个链表的头节点while (cur)     //遍历链表,清空链表中的所有元素{Node* next = cur->next;delete cur;cur = next;}_tables[i] = nullptr;}}

插入操作

链地址法插入操作的基本思路就是:

 1、选择合适的哈希函数,确定数组大小

2、通过哈希函数找到自己所对应的位置,并进行头插

3、当负载因子过大时进行扩容

		bool Insert(const pair<K, V>& kv){Hash hf;if (Find(kv.first))return false;//负载因子最大到1,到1时进行扩容//我们提供这样一个思路:如果数据真的非常多的时候,用链表来存储,因为要//                    考虑负载因子的原因,其实是比较浪费空间的,我们//                    可以把节点结构进行更改,改成红黑树的结构if (_n == _tables.size()){扩容//size_t newSize = _tables.size() * 2;//HashTable<K, V> newHT;//newHT._tables.resize(newSize);遍历旧表//for (size_t i = 0; i < _tables.size(); i++)//{//	Node* cur = _tables[i];//	while (cur)//	{//		newHT.Insert(cur->_kv);//		cur = cur->next;//	}//}//_tables.swap(newHT._tables);//方法二vector<Node*> newTables;newTables.resize(_tables.size() * 2);for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->next;size_t hashi = hf(cur->_kv.first) % newTables.size();cur->next = newTables[hashi];newTables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newTables);}size_t hashi = hf(kv.first) % _tables.size();     //哈希函数Node* newnode = new Node(kv);//头插newnode->next = _tables[hashi];_tables[hashi] = newnode;++_n;return true;}

查找操作

上面的插入操作中,我们首先就先用查找操作看是否已经有这个数据,因为哈希是不允许存在重复数据的,这里我们就来看一下这个查找操作,首先是先通过哈希函数找到对应的头节点,然后在对应的链表中进行查找

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

删除操作

删除操作也是先通过哈希函数找到删除元素的头节点,然后就是链表中元素的删除那一套操作

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

打印操作

链地址法我们一般需要观测的数据是链表个数,链表长度等(链表在这里也成为桶,即哈希桶),所以我们这里打印的是与链表个数、长度等相关的

		void Some(){size_t bucketSize = 0;        //桶的个数 size_t maxbucketLen = 0;      //最大桶长size_t sum=0;                 //总的元素个数double averagebucketLen = 0;  //平均桶长for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];if (cur){++bucketSize;}size_t bucketLen = 0;while (cur){++bucketLen;cur = cur->next;}sum += bucketLen;if (bucketLen > maxbucketLen){maxbucketLen = bucketLen;}}averagebucketLen = (double)sum / (double)bucketSize;cout << "桶的个数:" << bucketSize << endl;cout << "桶的最大长度:" << maxbucketLen << endl;cout << "平均桶的长度:" << averagebucketLen << endl;}

三、测试代码

我们给出几个测试用例检验一下上面的方法是否有误:

测试一:

	void TestHT1()   //测试插入,查找和删除操作是否有误{HashTable<int, int> ht;int a[] = { 4,14,24,34,5,7,1 };for (auto e : a){ht.Insert(make_pair(e, e));}cout << ht.Find(4) << endl;     //如果成功插入,这里会返回一个地址ht.Erase(4);                    //删除节点cout << ht.Find(4) << endl;     //删除后会返回nullptr}

运行结果:

测试二:

	void TestHT2()    //测试string{string arr[] = { "香蕉","甜瓜","苹果","香蕉","苹果","苹果" };HashTable<string, int> ht;for (auto e : arr){auto ret = ht.Find(e);if (ret)ret->_kv.second++;else{ht.Insert(make_pair(e, 1));}}ht.Some();    //通过桶的相关信息可以推断出插入情况}

运行结果:

四、总结

以上就是链地址法的内容,链地址法与开放地址法各有千秋,总的来说开放地址法时间复杂度更低,都是当数据过多时需要的空间多,链地址法节省空间但是效率上稍微偏低,在应用时要结合实际情况进行取舍

感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!

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

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

相关文章

Java NIO (一)

因工作需要我接触到了netty框架&#xff0c;这让我想起之前为夺高薪而在CSDN购买的Netty课程。如今看来&#xff0c;这套课程买的很值。这套课程中关于NIO的讲解&#xff0c;让我对Tomcat产生了浓厚的兴趣&#xff0c;于是我阅读了Tomcat中关于服务端和客户端之间连接部分的源码…

如何快速获取全网精准客流?揭秘不为人知的5大运营策略!

有同行所在的地方&#xff0c;就一定拥有咱们需要的客户。客户看的是结果&#xff0c;搜索的是问题&#xff0c;寻找的是答案。 如果没有付费流量&#xff0c;单纯靠搞免费流量&#xff0c;很多大厂的运营也会变得一文不值。一个牛逼的运营&#xff0c;不仅是会做付费流量&…

leetcode10 -- 正则表达式匹配

题目描述&#xff1a; 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 . 和 * 的正则表达式匹配。 . 匹配任意单个字符* 匹配零个或多个前面的那一个元素 所谓匹配&#xff0c;是要涵盖 整个 字符串 s的&#xff0c;而不是部分字符串。 示例 1&#xff1…

PDF转Word后不能修改怎么办?是什么原因呢?

平时在生活中&#xff0c;很多朋友都会有将PDF转换成Word文档的需求&#xff0c;因为一般情况下PDF文件是不能直接编辑修改的&#xff0c;所以只能通过这种方式来解决问题。但是近期&#xff0c;有部分用户在后台反馈说PDF转Word后不能修改怎么办呢&#xff1f;其实这个问题也是…

第1-3章Excel数据分析基础

文章目录 第1章&#xff1a;使用统计函数做数据分析1-1常用统计函数应用1-2条件统计函数1-3多条件统计函数1-4条件统计函数中的通配符1-5将条件统计函数中的条件数组化1-6单条件文本合并-新增函数1-7多条件与模仿通配符的文本合并 第2章&#xff1a;数据分析之合并计算2-1合并计…

备忘录系统

目录 一、 系统简介 1.简介 2需求分析 3 编程环境与工具 二、 系统总体设计 1 系统的功能模块图。 2 各功能模块简介 3项目结构 4 三、 主要业务流程 &#xff08;1&#xff09;用户及管理员登录流程图 &#xff08;2&#xff09;信息添加流程 &#xff0…

攻防世界 re 新手模式 2

IgniteMe 32位无壳 一些简单信息&#xff0c;看关键函数 可以得到v7[i]的值 比较简单的逆向 #include<stdio.h> #include<string.h> int main() {char flag[40];char s[40];char str[]"GONDPHyGjPEKruv{{pj]XrF";char ch[] {0x0D, 0x13, 0x17, 0x11,…

8、ViKings

难度 低->中 目标 root 2个flag 进行主机发现 netdiscover -i eth0 -r 192.168.189.0/24 kali 192.168.189.58 靶机 192.168.189.158 进行信息收集 nmap -T4 -sV -p- -A 192.168.189.158 开启了80和22&#xff0c;先探索web服务&#xff0c;没有思路再考虑ssh爆破 直…

react css module 不生效问题记录

背景&#xff1a;自己使用webpackreactcssless配置的项目框架&#xff0c;在使用过程中发现css module引入不生效。 import React from react import styles from ./index.module.less console.log(styles)//输出 undefinedwebpack配置了css-loader,less-loader,webpack默认cs…

Executable Code Actions Elicit Better LLM Agents

Executable Code Actions Elicit Better LLM Agents Github: https://github.com/xingyaoww/code-act 一、动机 大语言模型展现出很强的推理能力。但是现如今大模型作为Agent的时候&#xff0c;在执行Action时依然还是通过text-based&#xff08;文本模态&#xff09;后者JSO…

全网独一份!AI大模型学习训练资源...

国内AI赛道又爆了&#xff01; 继科大讯飞、阿里、华为等巨头公司发布AI产品后&#xff0c; 很多中小企业也陆续进场&#xff01; 人才市场上AI大模型工程师“一将难求”&#xff01; 甚至开出80k*16的高薪&#xff0c;挖掘AI大模型人才&#xff01; 如今大厂老板们&#…

arduino程序-变量操作(基础知识)

arduino程序-变量操作&#xff08;基础知识&#xff09; 1-7 arduino程序-变量操作太极创客网站类型资料变量类型范围超过上限会从下限开始长整型变量作用域&#xff08;全局变量&#xff0c;局部变量&#xff09;全局变量局部变量最后作业 1-7 arduino程序-变量操作 大家好这…

Ubuntu、centos、openEuler安装docker

目录 1.在 Ubuntu 上安装 Docker 1. 1 更新软件包 1. 2 安装必要的依赖 1.3 添加 Docker 的 GPG 密钥 1.4 添加 Docker 仓库 1.5 更新软件包 1.6 安装 Docker 1.7 启动并启用 Docker 服务 1.8 验证安装 1.9 运行测试容器 1.10 非 root 用户运行 Docker 2.在 CentOS…

多节点网络流量对比分析在业务性能分析中的应用

在现代企业网络环境中&#xff0c;网络流量分析已成为确保业务连续性和优化性能的关键工具。特别是多节点网络流量对比分析&#xff0c;通过对比不同节点间的流量&#xff0c;能有效发现潜在问题&#xff0c;提高网络运维效率。本文将探讨多节点网络流量对比分析在业务性能分析…

【数据结构】哈希表二叉搜索树详解

&#x1f48e; 欢迎大家互三&#xff1a;2的n次方_ &#x1f48e;所属专栏&#xff1a;数据结构与算法学习 &#x1f341;1. 二叉搜索树 二叉搜索树也称为二叉查找树或二叉排序树&#xff0c;是一种特殊的二叉树结构&#xff0c;它的特点是&#xff1a; 1. 若左树不为空&am…

顺序表的代码实现

顺序表的代码实现 1.认识什么是顺序表1.1顺序表的优缺点 2.实现顺序表代码准备3.顺序表的代码实现3.1 顺序表结构体的定义3.2 顺序表的初始化3.3 顺序表的销毁3.4 顺序表的输出打印3.5顺序表的扩容3.6 顺序表的头部插入(头插)3.7 顺序表的头部删除(头删)3.8 顺序表的尾部插入(尾…

2种常用的取消word文档”打开密码“方法

在日常工作中&#xff0c;我们有时会遇到需要取消Word文档“打开密码”的情况。无论是因为忘记密码&#xff0c;还是出于文档共享的需要&#xff0c;掌握几种有效的取消密码方法都显得尤为重要。以下是2种常用的方法来取消Word文档的“打开密码”。 方法一&#xff1a;文件另存…

二叉树--堆(上卷)

二叉树–堆&#xff08;上卷&#xff09; 树 树的概念与结构 树是⼀种⾮线性的数据结构&#xff0c;它是由 n&#xff08;n>0&#xff09; 个有限结点组成⼀个具有层次关系的集合。把它叫做 树是因为它看起来像⼀棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;⽽…

智慧医院信息系统思维导图

智慧医院信息系统 "思维导图智慧医院信息系统, 用一张图解析智慧医疗信息系统 本文转载&#xff1a;有了这个智慧医院信息系统思维导图&#xff0c;没人不明医疗信息化

是时候学习Grid布局了

一、序言 先说什么&#xff1f;当然先说大家最关心的兼容性了 CanIUse 嗯&#xff0c;对于非要兼容IE的开发者&#xff0c;我建议&#xff0c;量力而行&#xff01;兼容性还是不如Flex 当然&#xff0c;如果你flex够熟悉了&#xff0c;但却被一些布局有时候难倒&#xff0c;我…