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

【C++进阶07】哈希表and哈希桶

在这里插入图片描述

一、哈希概念

顺序结构以及平衡树中
元素关键码与存储位置没有对应关系
因此查找一个元素
必须经过关键码的多次比较
顺序查找时间复杂度为O(N)
平衡树中为树的高度,即O( l o g 2 N log_2 N log2N)

搜索效率 = 搜索过程中元素的比较次数
理想的搜索方法:不经任何比较
一次直接从表中获取想要的元素

构造一种存储结构
通过某种函数(hashFunc)使元素的存储位置
与它的关键码之间建立一一映射的关系
就能在查找时通过该函数直接找到该元素

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

该方式即为:
哈希(散列)方法
哈希方法中使用的转换函数称为:
哈希(散列)函数
构造出来的结构称为:
哈希表(Hash Table)(或者称散列表)

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

在这里插入图片描述

二、哈希冲突

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

把具有不同关键码
而具有相同哈希地址的数据元素称为“同义词”

11、21、31…数据经过哈希函数计算都为1
都插入在下标为1的地方便会冲突

在这里插入图片描述

三、哈希函数

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

哈希函数设计原则:

  • 哈希函数的定义域必须包括
    需要存储的全部关键码
    而如果散列表允许有m个地址时
    其值域必须在0到m-1之间
  • 哈希函数计算出来的地址能均匀分布在
    整个空间中
  • 哈希函数应该比较简单

常用哈希函数:

  1. 直接定址法
    取关键字的某个线性函数为散列地址:
    Hash(Key)= A*Key + B
    优点:简单、均匀
    缺点:需要事先知道关键字的分布情况
    使用场景:适合查找比较小且连续的情况
    面试题:字符串中第一个只出现一次字符

  2. 除留余数法
    设散列表中允许的地址数为m
    取一个不大于m
    但最接近或等于m的质数p作为除数
    按照哈希函数:
    Hash(key) = key% p(p<=m)
    将关键码转换成哈希地址

四、哈希冲突解决

解决哈希冲突两种常见方法:
闭散列和开散列

4.1 闭散列

闭散列:也叫开放定址法
当发生哈希冲突时
如果哈希表未被装满
说明哈希表中必然还有空位置
那么可以把key存放到
冲突位置的“下一个” 空位置中去

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

  1. 线性探测
    从发生冲突的位置开始
    依次向后探测
    直到寻找到下一个空位置为止

在这里插入图片描述
线性探测优点:实现简单

线性探测缺点:一旦发生哈希冲突
所有的冲突连在一起,容易产生数据“堆积”
即:不同关键码占据了可利用的空位置
使得寻找某关键码的位置需要许多次比较
导致搜索效率降低

  1. 二次探测

线性探测的缺陷是
产生冲突的数据堆积在一块
这与其找下一个空位置有关系
因为找空位置的方式就是挨着往后逐个去找

因此二次探测为了避免该问题
找下一个空位置的方法为:
H i H_i Hi = ( H 0 H_0 H0 + i 2 i^2 i2 )% m
或者: H i H_i Hi = ( H 0 H_0 H0 - i 2 i^2 i2 )% m
其中:i = 1,2,3…, H 0 H_0 H0是通过
散列函数Hash(x)对元素的关键码 key
进行计算得到的位置,m是表的大小

研究表明:当表的长度为质数且表装载因子
a不超过0.5时,新的表项一定能够插入
而且任何一个位置都不会被探查两次
因此只要表中有一半的空位置
就不会存在表满的问题
在搜索时可以不考虑表装满的情况
但在插入时必须确保表的装载因子a不超过
0.5,如果超出必须考虑增容
因此:比散列最大的缺陷就是空间利用率
比较低,这也是哈希的缺陷

4.2 开散列

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

如图:
将哈希地址相同的元素链接在同一个桶下面
在这里插入图片描述
在实际应用中
开散列比闭散列更实用

  1. 哈希桶负载因子更大
    空间利用率高
  2. 极端情况也有解决方案

哈希桶极端情况:
所有元素在同一个桶
为了避免这种情况
当桶超过一定高度
将该桶转换为红黑树结构

五、哈希桶的模拟实现

5.1 基本框架

namespace HashBucket // 哈希桶
{	template <class K, class V>struct HashNode{pair<K, V> _kv;HashNode<K, V>* _next; // 单链表的方式链接HashNode(const pair<K, V>& kv): _next(nullptr), _kv(kv){}};template <class K, class V>class HashTable{typedef HashNode<K, V> Node;public:private:vector<Node*> _tables;size_t _n = 0; // 存储的有效数据个数 };
}

5.2 插入元素

哈希桶的增容
若哈希表的大小为0
将哈希表的初始值设置为10

若哈希表的负载因子等于1
创建一个新表,大小是原表的两倍
用新表的哈希函数计算旧表的每个
元素在新表的映射位置
将旧表的每个元素头插进新表

bool Insert(const pair<K, V>& kv)
{// 去重, 插入之前先查找有没有相同的元素if (Find(kv.first))return false;// 负载因子 == 1时扩容if (_n == _tables.size()){// 哈希表大小为0,将哈希表初始值设为10size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;vector<Node*> newtables(newsize, nullptr);for (auto& cur : _tables){while (cur) // current不为空, 把挂着的数据一个一个移到新表{Node* next = cur->_next;size_t hashi = cur->_kv.first % newtables.size();// 头插到新表cur->_next = newtables[hashi];newtables[hashi] = cur;cur = next;}}_tables.swap(newtables);}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)
{if (_tables.size() == 0)return nullptr;size_t hashi = 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)
{size_t hashi = 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;}else{prev = cur;cur = cur->_next;}}return false;
}

✨✨✨✨✨✨✨✨
本篇博客完,感谢阅读🌹
如有错误之处可评论指出
博主会耐心听取每条意见
✨✨✨✨✨✨✨✨

相关文章:

【C++进阶07】哈希表and哈希桶

一、哈希概念 顺序结构以及平衡树中 元素关键码与存储位置没有对应关系 因此查找一个元素 必须经过关键码的多次比较 顺序查找时间复杂度为O(N) 平衡树中为树的高度&#xff0c;即O( l o g 2 N log_2 N log2​N) 搜索效率 搜索过程中元素的比较次数 理想的搜索方法&#xff1a…...

Go 语言实现冒泡排序算法的简单示例

以下是使用 Go 语言实现冒泡排序算法的简单示例&#xff1a; package mainimport "fmt"func bubbleSort(arr []int) {n : len(arr)for i : 0; i < n-1; i {for j : 0; j < n-i-1; j {if arr[j] > arr[j1] {// 交换元素arr[j], arr[j1] arr[j1], arr[j]}}}…...

JAVA 学习 面试(五)IO篇

BIO是阻塞I/O&#xff0c;NIO是非阻塞I/O&#xff0c;AIO是异步I/O。BIO每个连接对应一个线程&#xff0c;NIO多个连接共享少量线程&#xff0c;AIO允许应用程序异步地处理多个操作。NIO&#xff0c;通过Selector&#xff0c;只需要一个线程便可以管理多个客户端连接&#xff0…...

vue3相比vue2的效率提升

1、静态提升 2、预字符串化 3、缓存事件处理函数 4、Block Tree 5、PatchFlag 一、静态提升 在vue3中的app.vue文件如下&#xff1a; 在服务器中&#xff0c;template中的内容会变异成render渲染函数。 最终编译后的文件&#xff1a; 1.静态节点优化 那么这里为什么是两部分…...

web terminal - 如何在mac os上运行gotty

gotty可以让你使用web terminal的方式与环境进行交互&#xff0c;实现终端效果 假设你已经配置好了go环境&#xff0c;首先使用go get github.com/yudai/gotty命令获取可执行文件&#xff0c;默认会安装在$GOPATH/bin这个目录下&#xff0c;注意如果你的go版本比较高&#xff…...

机械设计-哈工大课程学习-螺纹连接

圆柱螺纹主要几何参数螺纹参数 ①外径&#xff08;大径&#xff09;&#xff0c;与外螺纹牙顶或内螺纹牙底相重合的假想圆柱体直径。螺纹的公称直径即大径。 ②内径&#xff08;小径&#xff09;&#xff0c;与外螺纹牙底或内螺纹牙顶相重合的假想圆柱体直径。 ③中径&#xff…...

ai绘画|stable diffusion的发展史!简短易懂!!!

手把手教你入门绘图超强的AI绘画&#xff0c;用户只需要输入一段图片的文字描述&#xff0c;即可生成精美的绘画。给大家带来了全新保姆级教程资料包 &#xff08;文末可获取&#xff09; 一、stable diffusion的发展史 本文目标&#xff1a;学习交流 对于熟悉SD的同学&#x…...

水塘抽样算法

水塘抽样算法 1、问题描述 最近经常能看到面经中出现在大数据流中的随机抽样问题 即&#xff1a;当内存无法加载全部数据时&#xff0c;如何从包含未知大小的数据流中随机选取k个数据&#xff0c;并且要保证每个数据被抽取到的概率相等。 假设数据流含有N个数&#xff0c;我…...

easyui渲染隐藏域<input type=“hidden“ />为textbox可作为分割条使用

最近在修改前端代码的时候&#xff0c;偶然发现使用javascript代码渲染的方式将<input type"hidden" />渲染为textbox时&#xff0c;会显示一个神奇的效果&#xff0c;这个textbox输入框并不会隐藏&#xff0c;而是显示未一个细条&#xff0c;博主发现非常适合…...

100天精通Python(实用脚本篇)——第113天:基于Tesseract-OCR实现OCR图片文字识别实战

文章目录 专栏导读1. OCR技术介绍2. 模块介绍3. 模块安装4. 代码实战4.1 英文图片测试4.2 数字图片测试4.3 中文图片识别 书籍分享 专栏导读 &#x1f525;&#x1f525;本文已收录于《100天精通Python从入门到就业》&#xff1a;本专栏专门针对零基础和需要进阶提升的同学所准…...

Go七天实现RPC

0.前言 本文是学习自7天用Go从零实现RPC框架GeeRPC | 极客兔兔 在此基础上&#xff0c;加入自己的学习过程与理解。 1.RPC 框架 RPC(Remote Procedure Call&#xff0c;远程过程调用)是一种计算机通信协议&#xff0c;允许调用不同进程空间的程序。RPC 的客户端和服务器可以…...

Elasticsearch:和 LIamaIndex 的集成

LlamaIndex 是一个数据框架&#xff0c;供 LLM 应用程序摄取、构建和访问私有或特定领域的数据。 LlamaIndex 是开源的&#xff0c;可用于构建各种应用程序。 在 GitHub 上查看该项目。 安装 在 Docker 上设置 Elasticsearch 使用以下 docker 命令启动单节点 Elasticsearch 实…...

QT基础篇(14)QT操作office实例

1.QT操作office的基本方式 通过QT操作Office软件&#xff0c;可以使用Qt的QAxObject类来进行操作。下面是一个例子&#xff0c;展示了通过Qt操作Excel的基本方式&#xff1a; #include <QApplication> #include <QAxObject>int main(int argc, char *argv[]) {QA…...

重拾计网-第四弹 计算机网络性能指标

ps&#xff1a;本文章的图片内容来源都是来自于湖科大教书匠的视频&#xff0c;声明&#xff1a;仅供自己复习&#xff0c;里面加上了自己的理解 这里附上视频链接地址&#xff1a;1.5 计算机网络的性能指标&#xff08;1&#xff09;_哔哩哔哩_bilibili ​​​ 目录 &#x…...

【Vue】Vue 路由的配置及使用

目录捏 前言一、路由是什么&#xff1f;1.前端路由2.后端路由 二、路由配置1.安装路由2.配置路由 三、路由使用1.route 与 router2. 声明式导航3. 指定组件的呈现位置 四、嵌套路由&#xff08;多级路由&#xff09;五、路由重定向1.什么是路由重定向&#xff1f;2.设置 redire…...

网络安全事件分级指南

一、特别重大网络安全事件 符合下列情形之一的&#xff0c;为特别重大网络安全事件&#xff1a; 1.重要网络和信息系统遭受特别严重的系统损失&#xff0c;造成系统大面积瘫痪&#xff0c;丧失业务处理能力。 2.国家秘密信息、重要敏感信息、重要数据丢失或被窃取、篡改、假…...

uniapp组件库SwipeAction 滑动操作 使用方法

目录 #平台差异说明 #基本使用 #修改按钮样式 #点击事件 #API #Props #Event 该组件一般用于左滑唤出操作菜单的场景&#xff0c;用的最多的是左滑删除操作。 注意 如果把该组件通过v-for用于左滑删除的列表&#xff0c;请保证循环的:key是一个唯一值&#xff0c;可以…...

YARN节点故障的容错方案

YARN节点故障的容错方案 1. RM高可用1.1 选主和HA切换逻辑 2. NM高可用2.1 感知NM节点异常2.2 异常NM上的任务处理 4. 疑问和思考4,1 RM感知NM异常需要10min&#xff0c;对于app来说是否太长了&#xff1f; 5. 参考文档 本文主要探讨yarn集群的高可用容错方案和容错能力的探讨。…...

C++后端笔记

C后端笔记 资源整理一、高级语言程序设计1.1 进制1.2 程序结构基本知识1.3 数据类型ASCII码命名规则变量间的赋值浮点型变量的作用字符变量常变量 const运算符 二、高级语言程序设计&#xff08;荣&#xff09; 资源整理 C后端开发学习路线及推荐学习时间 C基础知识大全 C那…...

JavaEE中什么是Web容器?

Web容器&#xff08;也称为Servlet引擎&#xff09;是一个用于执行Java Servlet和JSP的服务器端环境。它负责管理和执行在其上运行的Web应用程序。 Tomcat是Web容器 Apache Tomcat 是一个流行的开源的Web容器&#xff0c;它实现了Java Servlet和JavaServer Pages&#xff08;…...

MySQL 8.0 架构 之错误日志文件(Error Log)(1)

文章目录 MySQL 8.0 架构 之错误日志文件&#xff08;Error Log&#xff09;&#xff08;1&#xff09;MySQL错误日志文件&#xff08;Error Log&#xff09;MySQL错误日志在哪里Window环境Linux环境 错误日志相关参数log_error_services 参考 【声明】文章仅供学习交流&#x…...

51单片机实验课一

实验任务一&#xff1a;实现控制8个发光管的亮&#xff08;灭&#xff09; #include <REGX52.H> void Delay1ms(unsigned int xms) //11.0592MHz {unsigned char i, j;while(xms){xms--;i 12;j 169;do{while (--j);} while (--i);} } void main() {while(1){P20;//八…...

【.NET Core】多线程之线程池(ThreadPool)详解(一)

【.NET Core】多线程之线程池&#xff08;ThreadPool&#xff09;详解&#xff08;一&#xff09; 文章目录 【.NET Core】多线程之线程池&#xff08;ThreadPool&#xff09;详解&#xff08;一&#xff09;一、概述二、线程池的应用范围三、线程池特性3.1 线程池线程中的异常…...

圆的参数方程是如何推导的?

圆的参数方程是如何推导的? 1. 圆的三种参数表示2. 三角函数万能公式3. 回到圆的参数方程1. 圆的三种参数表示 已知圆的第一种参数方程为: x 2 + y 2 = r x^2+y^2=r x2+y2=r   圆的图像如下: 通过上图,不难理解,圆的参数方程还可以用三角函数表示,也就是第二种参数表…...

sqlmap使用教程(2)-连接目标

目录 连接目标 1.1 设置认证信息 1.2 配置代理 1.3 Tor匿名网络 1.4 检测WAF/IPS 1.5 调整连接选项 1.6 处理连接错误 连接目标 场景1&#xff1a;通过代理网络上网&#xff0c;需要进行相应配置才可以成功访问目标主机 场景2&#xff1a;目标网站需要进行身份认证后才…...

c++ http第一个服务

c http第一个服务 一、下载相关依赖&#xff1a;这是一个git开源项目 代码仓地址 二、演示代码&#xff0c;编译参数&#xff1a;g test.cpp -I/**** -lpthread #include <httplib.h> using namespace httplib;void wuhan(const Request &req, Response &res) …...

深入Android S (12.0) 探索Framework之输入子系统InputReader的流程

Framework层之输入系统 第一篇 深入Android S (12.0) 探索Framework之输入系统IMS的构成与启动 第二篇 深入Android S (12.0) 探索Framework之输入子系统InputReader的流程 文章目录 Framework层之输入系统前言一、基础知识1、输入子系统2、INotify 与 Epoll2.1、INotify 机制…...

【cucumber】cluecumber-report-plugin生成测试报告

cluecumber为生成测试报告的第三方插件&#xff0c;可以生成html测报&#xff0c;该测报生成需以本地json测报的生成为基础。 所以需要在测试开始主文件标签CucumberOptions中&#xff0c;写入生成json报告。 2. pom xml文件中加入插件 <!-- 根据 cucumber json文件 美化测…...

华为欧拉操作系统结合内网穿透实现固定公网地址SSH远程连接

文章目录 1. 本地SSH连接测试2. openEuler安装Cpolar3. 配置 SSH公网地址4. 公网远程SSH连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 欧拉操作系统(openEuler, 简称“欧拉”)是面向数字基础设施的操作系统,支持服务器、云计算、边缘openEuler是面向数字基础设施的操作系…...

加速 Selenium 测试执行最佳实践

Selenium测试自动化的主要目的是加快测试过程。在大多数情况下&#xff0c;使用 Selenium 的自动化测试比手动测试执行得特别好。在实际自动化测试实践中&#xff0c;我们有很多方式可以加速Selenium用例的执行。 我们可以选择使用不同类型的等待、不同类型的 Web 定位器、不同…...