数据结构——哈希详解

数据结构——哈希详解

目录

一、哈希的定义

二、六种哈希函数的构造方法

2.1 除留取余法

2.2 平方取中法

2.3 随机数法

2.4 折叠法

2.5 数字分析法

2.6 直接定值法

三、四种解决哈希冲突的方法

3.1 开放地址法

3.1.1 线性探测法

3.1.2 二次探测法

3.2 链地址法

3.3 再散列函数法

3.4 公共区溢出法

四、用代码解决链地址法


一、哈希的定义

顺序表/链表有一个共同特征,数据值本身和其存储位置之间是没有关系的,所以我们要查找/搜索一个值,只能一个一个的去比较,时间复杂度是O(n),

我们想把时间复杂度降下来,提供一种技术让我们数据值本身和存储关系之前有映射关系,这时我们查找值是否存在则直接根据这种映射关系计算得出其存储位置,这时只去要去计算得出的存储位置查看即可——这种技术就是散列技术也就是哈希,映射关系就是哈希函数f   

f(关键字key)=存储位置

哈希即是一种存储方法也是一种查找方法

哈希冲突定义:俩个或多个关键码Key1!=Key2但是通过哈希函数的计算得出的结果却相等,这种现象就是发生了哈希冲突

二、六种哈希函数的构造方法

2.1 除留取余法

原理

  • 哈希函数 h(k)=k mod m,其中 k 是键值,m 是哈希表的大小。

  • 通过取键值 k 除以 m 的余数来确定哈希值。

优点

  • 实现简单,计算速度快。

缺点

  • 如果键值分布不均匀,容易导致冲突。例如,当键值都是偶数时,若 m 是偶数,那么所有哈希值也都是偶数,会浪费一半的哈希表空间。

  • 对 m 的选择敏感,通常 m 选择为质数可以减少冲突。

应用场景

  • 适用于键值范围较大且分布较为均匀的场景,如简单的哈希表设计。

2.2 平方取中法

原理

  • 将键值 k 平方,然后从平方后的结果中取出中间几位数字作为哈希值。

  • 例如,键值 k=1234,平方后为 1522756,取中间几位(如 2275)作为哈希值。

优点

  • 能够较好地打乱键值的分布,减少冲突。

缺点

  • 如果键值较小,平方后的数字位数不够,可能需要补零,导致哈希值不够随机。

  • 计算平方操作相对耗时。

应用场景

  • 适用于对哈希值随机性要求较高的场景,但计算资源允许的情况下。

2.3 随机数法

原理

  • 使用伪随机数生成器(PRNG)根据键值生成随机数作为哈希值。

  • 通常需要一个种子值,种子值可以根据键值计算得到。

优点

  • 哈希值的随机性高,冲突概率低。

缺点

  • 随机数生成器的实现复杂,且需要保证每次计算结果一致(即相同的键值产生相同的哈希值)。

  • 如果随机数生成器质量不高,可能会导致哈希值分布不均匀。

应用场景

  • 适用于对哈希值随机性要求极高的场景,如密码学中的哈希函数。

2.4 折叠法

原理

  • 将键值分成若干部分,然后将这些部分进行折叠(相加、相减或按位运算)以生成哈希值。

  • 例如,键值 k=12345678,可以分成 1234 和 5678,然后相加得到 6912,再取模或者直接取后三位得到最终哈希值。

优点

  • 简单易实现,能够较好地处理较长的键值。

缺点

  • 如果键值的某些部分分布不均匀,可能会影响哈希值的分布。

  • 对折叠操作的选择敏感,不同的折叠方式可能导致不同的效果。

应用场景

  • 适用于键值较长且分布不均匀的场景,如字符串哈希。

2.5 数字分析法

原理

  • 分析键值的每一位数字(或字符),根据某种规则选择部分数字组合成哈希值。

  • 例如,键值 k=12345678,可以选择第 2、4、6 位数字(2、4、6),然后组合成 246 作为哈希值。

优点

  • 能够根据键值的分布特点进行优化,减少冲突。

缺点

  • 实现复杂,需要对键值的分布有先验知识。

  • 如果键值的分布变化较大,可能需要重新调整规则。

应用场景

  • 适用于对键值分布有明确了解的场景,如特定的数据库索引设计。

2.6 直接定值法

原理

  • 取关键字的线性函数值作为散列地址 f(key)=axkey+b 

  • 通常用于键值范围较小且连续的情况。

优点

  • 实现极其简单,没有冲突。

缺点

  • 如果键值范围较大,会浪费大量空间。

  • 不适用于键值范围较大的场景。

应用场景

  • 适用于键值范围较小且连续的场景,如小型数据库索引。

三、四种解决哈希冲突的方法

哈希冲突定义:俩个或多个关键码Key1!=Key2但是通过哈希函数的计算得出的结果却相等,这种现象就是发生了哈希冲突

3.1 开放地址法

3.1.1 线性探测法

原理:当发生哈希冲突时,从冲突位置开始,按照线性顺序依次探测下一个位置,向右探测,直到找到空闲位置为止。即若哈希地址为 h(key) 的位置已被占用,则依次探测 h(key)+1、h(key)+2、h(key)+3…… 直到找到空闲位置。

优点:实现简单,容易理解和编程实现。

缺点:容易出现 “聚集” 现象,即连续的多个空闲位置被占用,形成一个聚集区,导致后续元素查找和插入时需要探测更多的位置,效率降低。

公式:f(key)=(f(key)+d)mod m      d=1,2,3,4,5…

3.1.2 二次探测法

使用线性探测法会发生堆积,我们想要探测的时候即向左也向右探测并且每次探测幅度尽可能变化,呈指数变化 eg:1,-1,4,-4,9,-9

增加平方运算是为了不让关键字都聚集在某一个区域

优点:能有效减少聚集现象,提高哈希表的性能。

缺点:不能探测到哈希表中的所有位置,可能会出现无法找到空闲位置的情况,特别是当哈希表大小不是合适的数值时。

3.2 链地址法

其核心思想是将所有哈希地址相同的元素都链接到同一个链表中。在哈希表中,每个位置对应一个链表,当发生哈希冲突(即不同的关键字通过哈希函数计算得到相同的哈希地址)时,将这些冲突的元素依次插入到对应的链表中

优点

  • 处理冲突简单,不需要探测。

  • 删除元素容易,只需从链表中移除即可。

缺点

  • 需要额外的空间来存储链表。

  • 当一个槽位的链表很长时,搜索效率会降低。

3.3 再散列函数法

再散列函数法使用两个不同的哈希函数。第一个哈希函数 h1(key) 用于计算元素的初始哈希地址。当该地址发生冲突时,使用第二个哈希函数 h2(key) 来确定下一个探测位置的步长,从而在哈希表中寻找下一个可用的位置。

通过这种方式,不断尝试新的位置,直到找到一个空槽来插入元素,或者确定该元素不存在于哈希表中。

3.4 公共区溢出法

不冲突放到基本表中,冲突就放到溢出表中,基本表中都没有数据为空说明溢出表中肯定没有,基本表由哈希函数构成,溢出表由顺序存储构成先来先到,

适用于哈希冲突相对较少的场景

四、用代码解决链地址法

.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "hash_List_address.h"
#include <stdlib.h>//0.哈希函数
int Hash(ELEM_TYPE val)
{return val % INITSIZE;
}//1.初始化
void Init_List_Address(List_address* pla)
{for (int i = 0; i < INITSIZE; i++){Init_List(&pla->arr[i]);}
}//2.插入值(头插)
bool Insert(List_address* pla, ELEM_TYPE val)
{//assertint index = Hash(val);struct Node* pnewnode = (Node*)malloc(sizeof(Node));if (pnewnode == NULL){return false;}pnewnode->data = val;pnewnode->next = pla->arr[index].next;pla->arr[index].next = pnewnode;return true;
}//3.删除值
bool Del(List_address* pla, ELEM_TYPE val)
{struct Node* q = Search(pla, val);if (q == NULL)return false;//此时,代码执行到这里,证明val值节点存在在index下标里面的单链表上int index = Hash(val);struct Node* p = &pla->arr[index];for (; p->next != q; p = p->next);//此时,代码执行到这里,证明p和q都就位p->next = q->next;free(q);q = NULL;return true;
}//4.查找值
struct Node* Search(List_address* pla, ELEM_TYPE val)
{//assertint index = Hash(val);struct Node* q = pla->arr[index].next;for (; q != NULL; q = q->next){if (q->data == val){break;}}return q;}//5.打印
void Show(List_address* pla)
{for (int i = 0; i < INITSIZE; i++){printf("第%d行:", i);struct Node* p = pla->arr[i].next;for (; p != NULL; p=p->next){printf("%d->", p->data);}printf("\n");}
}int main()
{List_address head;Init_List_Address(&head);Insert(&head, 12);Insert(&head, 67);Insert(&head, 56);Insert(&head, 16);Insert(&head, 25);Insert(&head, 37);Insert(&head, 22);Insert(&head, 29);Insert(&head, 15);Insert(&head, 47);Insert(&head, 48);Insert(&head, 34);Show(&head);Del(&head, 25);Del(&head, 12345);Show(&head);return 0;
}

.h 

#pragma oncetypedef int ELEM_TYPE;
//链地址法有效节点结构体设计:
typedef struct List_Node
{ELEM_TYPE data;struct List_Node* next;
}List_Node;#include "list.h"//链地址法整个的辅助节点结构体设计:
#define INITSIZE 12
typedef struct List_address
{struct Node arr[INITSIZE];
}List_address;//0.哈希函数
int Hash(ELEM_TYPE val);//1.初始化
void Init_List_Address(List_address* pla);//2.插入值
bool Insert(List_address* pla, ELEM_TYPE val);//3.删除值
bool Del(List_address* pla, ELEM_TYPE val);//4.查找值
struct Node* Search(List_address* pla, ELEM_TYPE val);//5.打印
void Show(List_address* pla);

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

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

相关文章

使用U盘安装 ubuntu 系统

1. 准备U 盘制作镜像 1.1 下载 ubuntu iso https://ubuntu.com/download/ 这里有多个版本以供下载&#xff0c;本文选择桌面版。 1.2 下载rufus https://rufus.ie/downloads/ 1.3 以管理员身份运行 rufus 设备选择你用来制作启动项的U盘&#xff0c;不能选错了&#xff1b;点…

RadioMaster POCKET遥控器进入ExpressLRS界面一直显示Loading的问题解决方法

RadioMaster POCKET遥控器进入ExpressLRS界面一直显示Loading的问题解决方法 问题描述解决方法 问题描述 有一天我发现我的 RadioMaster POCKET 遥控器进入 ExpressLRS 设置界面时&#xff0c;界面却一直停留在 “Loading” 状态&#xff0c;完全无法进入设置界面。 我并没有…

计算机网络 - 三次握手相关问题

通过一些问题来讨论 TCP 协议中的三次握手机制 说一下三次握手的大致过程&#xff1f;为什么需要三次握手&#xff1f;2 次不可以吗&#xff1f;第三次握手&#xff0c;可以携带数据吗&#xff1f;第二次呢&#xff1f;三次握手连接阶段&#xff0c;最后一次ACK包丢失&#xf…

【RabbitMQ】核心概念和工作流程

文章目录 RabbitMQ 工作流程流程图 Producer 和 ConsumerConnecting 和 ChannelVirtual hostQueueExchangeRabbitMQ 工作流程 RabbitMQ 工作流程 流程图 RabbitMQ 就是一个生产者/消费者模型 Producer 就是生产者、Consumer 就是消费者Broker 是 RabbitMQ 服务器生产者和消费…

龙虎榜——20250414

今天缩量上涨有些乏力&#xff0c;压力位还在~ 2025年4月14日龙虎榜行业方向分析 一、核心主线方向 黄金与贵金属&#xff08;避险逻辑强化&#xff09; • 驱动逻辑&#xff1a;国际地缘冲突持续升温&#xff08;如中东局势、台海动态&#xff09;&#xff0c;叠加美国特朗普…

蔚来汽车智能座舱接入通义大模型,并使用通义灵码全面提效

为加速AI应用在企业市场落地&#xff0c;4月9日&#xff0c;阿里云在北京召开AI势能大会。阿里云智能集团资深副总裁、公共云事业部总裁刘伟光发表主题演讲&#xff0c;大模型的社会价值正在企业市场释放&#xff0c;阿里云将坚定投入&#xff0c;打造全栈领先的技术&#xff0…

探索 Go 与 Python:性能、适用场景与开发效率对比

1 性能对比&#xff1a;执行速度与资源占用 1.1 Go 的性能优势 Go 语言被设计为具有高效的执行速度和低资源占用。它编译后生成的是机器码&#xff0c;能够直接在硬件上运行&#xff0c;避免了 Python 解释执行的开销。 以下是一个用 Go 实现的简单循环计算代码&#xff1a; …

虚幻引擎 Anim To Tex| RVT | RT

本文上篇分为4个部分&#xff1a;动画驱动材质&#xff0c;虚拟纹理&#xff0c;Rendertarget&#xff0c;以及其他杂项的地编ta干货整理。&#xff08;其中RT部分基本为UOD重要截图摘录&#xff09; 本文下篇为&#xff1a;skylight和directional light的区别&#xff0c;未完…

kingbase权限管理

1. kingbase模式权限管理 1.1授予用户对模式的权限 以具有足够权限的用户登录后&#xff0c;执行以下 SQL 语句来授予用户对模式的相应权限。假设你要授予用户 your_user 对模式 your_schema 的使用权限&#xff1a; sql -- 授予用户使用模式的权限 GRANT USAGE ON SCHEMA …

9.thinkphp的请求

请求对象 当前的请求对象由think\Request类负责&#xff0c;该类不需要单独实例化调用&#xff0c;通常使用依赖注入即可。在其它场合则可以使用think\facade\Request静态类操作。 项目里面应该使用app\Request对象&#xff0c;该对象继承了系统的think\Request对象&#xff…

Java从入门到“放弃”(精通)之旅——方法的使用⑤

Java从入门到“放弃”&#xff08;精通&#xff09;之旅&#x1f680;——方法的使用⑤ &#x1f4d6;引言&#xff1a; 在编程领域&#xff0c;代码如同精密的齿轮相互咬合驱动程序运转。随着项目规模渐长&#xff0c;重复的代码片段如同冗余的齿轮&#xff0c;不仅增加负重…

鸿蒙NEXT开发格式化工具类(ArkTs)

import { i18n } from kit.LocalizationKit;/*** 格式化工具类* 提供电话号码格式化、归属地查询、字符转换等功能。* author: 鸿蒙布道师* since: 2025/04/14*/ export class FormatUtil {/*** 判断传入的电话号码格式是否正确。* param phone - 待验证的电话号码* param coun…

[Python基础速成]2-模块与包与OOP

上篇➡️[Python基础速成]1-Python规范与核心语法 目录 Python模块创建模块与导入属性__name__dir()函数标准模块 Python包类类的专有方法 对象继承多态 Python模块 Python 中的模块&#xff08;Module&#xff09;是一个包含 Python 定义和语句的文件&#xff0c;文件名就是模…

OSI参考模型和TCP/IP模型

1.OSI参考模型 OSI模型&#xff1a; OSI参考模型有7层&#xff0c;自下而上依次为物理层&#xff0c;数据链路层&#xff0c;网络层&#xff0c;传输层&#xff0c;会话层&#xff0c;表示层&#xff0c;应用层。&#xff08;记忆口诀&#xff1a;物联网叔会用&#xff09;。低…

linux Shell编程之循环语句(三)

目录 一. for 循环语句 1. for语句的结构 2. for 语句应用示例 (1) 根据姓名列表批量添加用户 (2) 根据 IP 地址列表检查主机状态 二. 使用 while 循环语句 1. while 语句的结构 2. while 语句应用示例 (1) 批量添加规律编号的用户 (2) 猜价格游戏 三. until 循环语…

最新扣子实战教程,利用扣子平台通过在线表格记录,批量生图,再也不要一条条的粘贴提示词了

1、功能描述 大家好&#xff0c;我是涛涛。今天我要给大家讲解如何在扣子平台上对接飞书电子表格。由于多维表格相对复杂&#xff0c;而很多业务场景其实只需要电子表格就能满足&#xff0c;因此今天我们将演示如何在扣子平台上读取飞书电子表格并批量生成图片。 先看效果&am…

java -jar指定类加载

在 Java 中&#xff0c;使用 java -jar 命令运行 JAR 文件时&#xff0c;默认会加载 JAR 文件的 MANIFEST.MF 文件中指定的 Main-Class。如果你想在运行时指定一个类来加载&#xff0c;可以通过以下方式实现&#xff1a; 方法 1&#xff1a;直接指定类路径和类名 如果你不想使用…

多模态思维链(Multimodal Chain of Thought, MCoT)六大技术支柱在医疗领域的应用

多模态思维链(Multimodal Chain of Thought, MCoT)通过整合文本、图像、视频等多模态数据,结合逻辑推理与深度学习技术,在医疗领域展现出强大的应用潜力。其六大技术支柱在医疗场景中的具体应用如下: 一、推理构建视角:医学诊断的流程优化 MCoT通过多模态推理链生成技术…

从文本到视频:基于扩散模型的AI生成系统全解析(附PyTorch实现)

当语言遇见动态视觉 "用文字生成电影场景"曾是科幻作品中的幻想&#xff0c;如今借助扩散模型&#xff08;Diffusion Models&#xff09;正逐步成为现实。本文将手把手带你实现一个创新的文本到视频生成系统&#xff0c;通过深度解析扩散模型原理&#xff0c;结合独…

科普:如何通过ROC曲线,确定二分类的“理论阈值”

在二分类问题中&#xff0c;已知预测概率&#xff08;如逻辑回归、神经网络输出的概率值&#xff09;时&#xff0c;阈值的选择直接影响分类结果&#xff08;正/负样本判定&#xff09;。 一、实践中的阈值选择方法 1. 基于业务目标的调整 最大化准确率&#xff1a;适用于样…