C++ 哈希

文章目录

  • 哈希概念
  • 哈希冲突
  • 哈希函数
    • 闭散列
    • 闭散列实现
    • 开散列
    • 开散列实现
  • 字符串Hash函数

哈希概念

因为,顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,
因此在查找一个元素时,必须要经过关键码的多次比较。
顺序查找时间复杂度为O(N),平衡树中为树的高度即O(logN)
如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立
一一映射的关系,那么在查找时通过该函数可以很快找到该元素

  • 插入元素
    根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
  • 搜索元素
    对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置
    取元素比较,若关键码相等,则搜索成功

该方式即为哈希(散列)方法,
构造出来的结构称为哈希表

集合{1,7,6,4,5,9};
哈希函数设置为:hash(key) = key % capacity; capacity为存储元素底层空间总的大小。

在这里插入图片描述

如果按照上述方式插入14会发生什么???
发现4这个位置已0经被占用了
再映射到这个位置明显是不对的,
这就是所谓的哈希冲突。

哈希冲突

不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突。

哈希冲突如何处理呢?

哈希函数

哈希冲突的一个原因可能是:
哈希函数设计不够合理

在这里插入图片描述

但是无论如何设计哈希函数,
都不能完全保证不同的值映射到同一位置,
所以有了闭散列和开散列两种处理方法

闭散列

也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去,对于上述例子添加14后,14就会被放在下标为8的位置

如何寻找下一个空位置呢?

  1. 线性探测
    在这里插入图片描述

删除操作
采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索;

哈希表中的元素如果只是原生数据类型,我们将4删除后,再查找4是找不到的,但是此时去查找14也会找不到,因为14本来应该映射到4位置,但是由于哈希冲突跑到了8位置.

如何解决呢?

给每一个位置加上一个状态枚举分别代表此位置是空,被删除还是有数

// EMPTY空, EXIST有元素, DELETE删除
enum State {EMPTY, EXIST, DELETE};

闭散列实现

enum State
{EMPTY, // 空EXIST, // 存在DELETE // 删除
};template <class K, class V>
struct HashData
{pair<K, V> _kv;State _state;HashData(const pair<K, V> &kv = make_pair(0, 0)): _kv(kv), _state(EMPTY){}
};template <class K>
struct HashFunc
{size_t operator()(const K &key){return key;}
};
// 模版特化
template <>
struct HashFunc<string>
{size_t operator()(const string &key){// BKDR Hash思想size_t hash = 0;for (size_t i = 0; i < key.size(); ++i){hash *= 131;hash += key[i]; // 转成整形}return hash;}
};template <class K, class V, class Hash = HashFunc<K>>
class HashTable
{
private:vector<HashData<K, V>> _table; // HashData的封装size_t _size = 0;              // 存储的关键字的个数
};bool Insert(const pair<K, V> &kv)
{if (_table.size() == 0 || 10 * _size / _table.size() >= 7) // 扩容{size_t newSize = _table.size() == 0 ? 10 : _table.size() * 2;HashTable<K, V> newHT;newHT._table.resize(newSize);// 旧表的数据映射到新表for (auto e : _table){if (e._state == EXIST){newHT.insert(e._kv);}}_table.swap(newHT._table);}size_t index = kv.first % _table.size();// 线性探测while (_table[index]._state == EXIST){index++;index %= _table.size(); // 过大会重新回到起点}_table[index]._kv = kv;_table[index]._state = EXIST;_size++;return true;
}HashData<K, V> *Find(const K &key)
{if (_table.size() == 0)return nullptr;size_t index = key % _table.size(); // string类不能取模,需要一个仿函数HashFuncsize_t start = index;while (_table[index]._state != EMPTY){if (_table[index]._kv.first == key && _table[index]._state == EXIST)return &_table[index];index++;index %= _table.size();if (index == start) // 全是DELETE时,必要时会breakbreak;}return nullptr;
}bool Erase(const K &key)
{HashData<K, V> *ret = Find(key);if (ret){ret->_state = DELETE;--_size;return true;}return false;
}

开散列

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

在这里插入图片描述
哈希桶其实是一个链表指针,其中每个桶中存放的都是发生哈希冲突的数据。
在这里插入图片描述

开散列实现

//HashFunc<int>
template<class K>
struct HashFunc
{size_t operator()(const K& key){return (size_t)key;}
};//HashFunc<string>
template<>
struct HashFunc<string>
{size_t operator()(const string& key){// BKDRsize_t hash = 0;for (auto e : key){hash *= 31;hash += e;}cout << key << ":" << hash << endl;return hash;}
};template <class K, class V>
struct HashNode
{HashNode *_next;pair<K, V> _kv;HashNode(const pair<K, V> &kv): _kv(kv), _next(nullptr){}
};template <class K, class V, class Hash = HashFunc<K>>
class HashTable
{typedef HashNode<K, V> Node;public:HashTable(){_tables.resize(10);}~HashTable(){for (size_t i = 0; i < _tables.size(); i++){Node *cur = _tables[i];while (cur){Node *next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}bool Insert(const pair<K, V> &kv){if (Find(kv.first))return false;// 负载因子最大到1if (_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);}size_t hashi = 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 NULL;}bool Erase(const K &key){Hash hf;size_t hashi = hf(key) % _tables.size();Node *prev = nullptr;Node *cur = _tables[hashi];while (cur){if (cur->_kv.first == key){if (prev == nullptr){_tables[hashi] = cur->_next;}else{prev->_next = cur->_next;}delete cur;return true;}prev = cur;cur = cur->_next;}return false;}private:vector<Node *> _tables;size_t _n = 0;
};

字符串Hash函数

当我们对key取模的时候如果模的不是整形那我们无法处理,这时我们使用一种字符串Hash函数将其转化为整形,例如string,如下

template<>
struct HashFunc<string>
{size_t operator()(const string& key){// BKDRsize_t hash = 0;for (auto e : key){hash *= 31;hash += e;}cout << key << ":" << hash << endl;return hash;}
};

最后,提供一篇字符串Hash函数的文章
字符串Hash函数

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

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

相关文章

ROS摄像机标定

文章目录 一、环境准备二、摄像头标定2.1 为什么要标定2.2 标定前准备2.2.1 标定板2.2.2 摄像头调焦 2.3 开始标定2.4 测试标定结果 总结参考资料 一、环境准备 安装usb_cam相机驱动 sudo apt-get install ros-noetic-usb-cam 安装标定功能包 sudo apt-get install ros-noet…

uniapp获取当前位置及检测授权状态

uniapp获取当前位置及检测授权定位权限 文章目录 uniapp获取当前位置及检测授权定位权限效果图创建js文件permission.jslocation.js 使用 效果图 Android设备 点击 “设置”&#xff0c;跳转应用信息&#xff0c;打开“权限即可”&#xff1b; 创建js文件 permission.js 新建…

一觉醒来 AI科技圈发生的大小事儿 04月27日

⏩阿里智能体“组装工厂”开源&#xff01;0经验搞定上万Agent并发 阿里巴巴通义实验室开源了多智能体编程框架与开发平台AgentScope&#xff0c;旨在提供高易用的编程体验、稳定可靠的运行时保障&#xff0c;并且为开发者提供了分布式和多模态的技术支持。AgentScope提供了拖…

哈夫曼编码---一种无损数据压缩算法

哈夫曼编码是一种无损数据压缩算法&#xff0c;该算法在数据压缩&#xff0c;存储和网络传输等领域广泛引用&#xff0c;对互联网的发展也产生了深远的影响。 大家熟知的数据无损压缩软件&#xff0c;如WinRAR&#xff0c;gzip&#xff0c;bzip&#xff0c;lzw&#xff0c;7-z…

Linux操作系统基础开发工具的使用——vim,gcc/g++,MakeFile,gdb,yum

目录 一&#xff0c;vim&#xff08;Linux常用文本编辑器&#xff09; 1.1 关于vim 1.2 vim的三种常用模式 1.3 各种模式的切换&#xff08;一图览&#xff09; 1.4 vim命令模式各命令集合 1.5 vim底行模式各命令集合 1.6 vim配置 二&#xff0c;gcc/g&#xff08;Linu…

【鸿蒙应用】理财App

目录 第一节项目讲解项目介绍 第二节&#xff1a;项目创建登录静态框架编写登录页面设稿新建项目控制台添加项目Login页面封装标题组件 第三节&#xff1a;登录页静态表单编写第四节—内容页架构分析底部栏组件第五节—底部栏组件切换第六节&#xff1a;首页静态页编写第七节&a…

STM32与OLED显示屏通信(四针脚和七阵脚)

系列文章目录 STM32单片机系列专栏 C语言术语和结构总结专栏 文章目录 1. 单片机调试 2. OLED简介 3. 接线 4. OLED驱动函数 4.1 四针脚版本 OLED.c OLED.h OLED_Font.h 4.2 七针脚版本 引脚连接 OLED.c OLED.h OLED_Font.h 5. 主函数 工程文件模板 1. 单片机…

Spark和Hadoop的安装

实验内容和要求 1&#xff0e;安装Hadoop和Spark 进入Linux系统&#xff0c;完成Hadoop伪分布式模式的安装。完成Hadoop的安装以后&#xff0c;再安装Spark&#xff08;Local模式&#xff09;。 2&#xff0e;HDFS常用操作 使用hadoop用户名登录进入Linux系统&#xff0c;启动…

CSS 之 transition过渡动画

一、简介 ​ CSS 制作 Web 动画有两种方式&#xff1a; 帧动画&#xff08;Keyframe Animation&#xff09;和过渡动画&#xff08;Transition Animation&#xff09;。针对不同的业务场景中&#xff0c;我们应该选择不同的动画方式&#xff0c;通常来说&#xff1a;对于交互元…

从虚拟化走向云原生,红帽OpenShift“一手托两家”

汽车行业已经迈入“软件定义汽车”的新时代。吉利汽车很清醒地意识到&#xff0c;只有通过云原生技术和数字化转型&#xff0c;才能巩固其作为中国领先汽车制造商的地位。 和很多传统企业一样&#xff0c;吉利汽车在走向云原生的过程中也经历了稳态业务与敏态业务并存带来的前所…

微信第三方开放平台,实现代公众号保留排版样式和图片发布文章

大家好&#xff0c;我是小悟 要想实现代公众号发布文章的功能&#xff0c;就得接入富文本编辑器&#xff0c;市面上富文本编辑器有很多&#xff0c;轻量的、重量的都有。 从开发者的角度&#xff0c;自然把轻量作为第一选择&#xff0c;因为好对接&#xff0c;怎么方便怎么来…

【Python】爬虫-基础入门

目录 一、什么是爬虫 二、爬虫的主要用途 三、学会爬虫需要掌握的技能 四、爬虫使用的语言 五、编写爬虫需要的库&#xff0c;以python为例 六、爬虫示例-python 示例一 示例二 示例三 一、什么是爬虫 爬虫&#xff0c;又称网络爬虫或网页爬虫&#xff0c;是一种用来自…

Windows电脑中护眼(夜间)模式的开启异常

我的电脑是联想小新16pro&#xff0c;Windows11版本。之前一直可以正常使用夜间模式&#xff0c;但是经过一次电脑的版本更新之后&#xff0c;我重启电脑发现我的夜间模式不能使用了。明明显示开启状态&#xff0c;但是却不能使用&#xff0c;电脑还是无法显示夜间模式。 询问…

Drive Scope for Mac:硬盘健康监测分析工具

Drive Scope for Mac是一款专为Mac用户设计的硬盘健康监测与分析工具&#xff0c;致力于保障用户的数据安全。这款软件功能强大且操作简便&#xff0c;能够实时检测硬盘的各项指标&#xff0c;帮助用户及时发现并解决潜在问题。 Drive Scope for Mac 1.2.23注册激活版下载 Driv…

图像处理:乘法滤波器(Multiplying Filter)和逆FFT位移

一、乘法滤波器&#xff08;Multiplying Filter&#xff09; 乘法滤波器是一种以像素值为权重的滤波器&#xff0c;它通过将滤波器的权重与图像的像素值相乘&#xff0c;来获得滤波后的像素值。具体地&#xff0c;假设乘法滤波器的权重为h(i,j)&#xff0c;图像的像素值为f(m,…

基于CANoe从零创建以太网诊断工程(2)—— TCP/IP Stack 配置的三种选项

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

经典的目标检测算法有哪些?

一、经典的目标检测算法有哪些&#xff1f; 目标检测算法根据其处理流程可以分为两大类&#xff1a;One-Stage&#xff08;单阶段&#xff09;算法和Two-Stage&#xff08;两阶段&#xff09;算法。以下是一些经典的目标检测算法&#xff1a; 单阶段算法: YOLO (You Only Loo…

前端三大件速成 01 HTML

文章目录 一、前端基础知识二、标签1、什么是标签2、标签的属性3、常用标签&#xff08;1&#xff09;声明&#xff08;2&#xff09;注释&#xff08;3&#xff09;html 根标签&#xff08;3&#xff09;head标签&#xff08;4&#xff09;body标签 三、特殊字符四、其他标签1…

xhEditor实现WORD粘贴图片自动上传

1.下载示例&#xff1a; 从官网下载 http://www.ncmem.com/webapp/wordpaster/versions.aspx 从gitee中下载 https://gitee.com/xproer/wordpaster-php-xheditor1x 2.将插件目录复制到项目中 3.引入插件文件 定义插件图标 初始化插件&#xff0c;在工具栏中添加插件按钮 效果…

Kafka源码分析(四) - Server端-请求处理框架

系列文章目录 Kafka源码分析-目录 一. 总体结构 先给一张概览图&#xff1a; 服务端请求处理过程涉及到两个模块&#xff1a;kafka.network和kafka.server。 1.1 kafka.network 该包是kafka底层模块&#xff0c;提供了服务端NIO通信能力基础。 有4个核心类&#xff1a;…