数据结构与算法基础-学习-20-查找之散列表(HASH TABLE)

news2024/11/16 3:54:43

目录

 

目录

一、基本思想

二、术语

1、散列方法

2、散列函数

3、散列表

4、冲突

5、同义词

三、如何减少哈希冲突

四、构造散列函数需考虑的情况

五、散列函数的构造方法

1、直接定址法

2、除留余数法

六、如何处理哈希冲突

1、开地址法

2、拉链法

七、散列表查找效率分析

八、宏定义

九、结构体定义

1、HashTabElemType

2、HashNode

3、HashTable

十、函数定义

1、初始化哈希表

 2、哈希函数(除留余数法)

 3、添加节点

 4、插入数据到哈希表

 5、哈希表搜索值

十一、Linux环境测试


 


 

一、基本思想

记录存储位置和关键字之间的对应关系。

HASH TABLE也可以称为哈希表、散列表、杂凑表。

 

二、术语

 

1、散列方法

选取某个函数,用该函数按照关键字计算元素的存储位置,以此存放。

在查找时,用相同的函数计算传入参数的地址位置,再将传入参数与地址单元中的元素进行比较,判断查找是否成功。

 

2、散列函数

散列方法中使用的转换函数。

 

3、散列表

按照上述思想构造的表。

 

4、冲突

不同关键码映射到同一个散列地址。

就是说不同输入参数,经过散列函数得到的散列值是一样的。

 

5、同义词

具有相同函数值的多个关键字。

例如:输入参数a和输入参数b,经过hash计算得到的hash值相同,则a和b称为同义词。

 

三、如何减少哈希冲突

哈希冲突是不可避免的,只能尽量减少冲突的发生。

 

1、根据不同的哈希算法适当的加宽哈希表的长度。

 

2、选择哈希算法时也要考虑其计算出的哈希值是均匀分布的,但也要考虑算法的转换效率和空间浪费问题。

 

四、构造散列函数需考虑的情况

 

1、散列函数执行效率。

 

2、散列函数的关键字长度。

 

3、散列表的宽度。

 

4、关键字的分布情况。

 

5、查找频率。

 

五、散列函数的构造方法

构造方法名介绍情况
直接定址法介绍实现原理。
数字分析法本文不介绍。
平方取中法本文不介绍。
折叠法本文不介绍。
除留余数法介绍实现原理和C源码实现。
随机数法本文不介绍。

1、直接定址法

gif.latex?Hash%28Key%29%20%3D%20A%20*%20Key%20+%20B

其中A和B为常数。假设A=2,B=1。

测试数据为正整数:{1,3,2};

1和2之间空出两个空间,且不能存下其他正整数。

802890d42d224e3886761ce1ec2ab542.png

 优点:以关键码Key的某个线性函数值为散列地址,不会产生冲突的情况。

 

缺点:需要占用连续的地址空间,空间效率低。

 

2、除留余数法

gif.latex?Hash%28Key%29%20%3D%20Key%20%25%20P

 

假设表长为N,则P<=N且为质数(质数指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。)

其中P为整数,%为余除,假设P=7。

测试数据为正整数:{1,3,2,8};

bdf89b2657f946e18e7405d3b89c32d4.png

优点:空间使用效率高。

 

缺点:可能会出现哈希冲突。

 

六、如何处理哈希冲突

方法名介绍情况
开放定址法(开地址法)介绍实现原理。
链地址法(拉链法)介绍实现原理和C源码实现。
再散列法(双散列函数法)本文不介绍。
建立一个公共溢出区本文不介绍。

1、开地址法

基本思想:在发生冲突时会去寻找下一个空的地址,只要散列表的宽度足够,一定能找到空的地址存储下。

除留余数法为例:gif.latex?Hi%20%3D%20%28Hash%28Key%29%20&plus;%20Di%29%20%25%20P

其中Di为增量序列。由于Di的不同,可以划分为三种常用方法。

方法名描述
线性探测法Di为1,2,.....,P-1线性序列。
二次探测法Di为1^2,-1^2,2^2,-2^2,......,Q^2二次序列。
伪随机探测法Di为伪随机序列。

 

bdf89b2657f946e18e7405d3b89c32d4.png

这里就拿线性探测法简单举例说明一下,利用gif.latex?Hash%28Key%29%20%3D%20Key%20%25%20P

P等于7进行计算,发现8和1有哈希冲突, 这时用gif.latex?Hi%20%3D%20%28Hash%28Key%29%20&plus;%20Di%29%20%25%20P

,Di为1,也就是((8 % 7) + 1) % 7,等于2,但2号索引位存了2,还是冲突,继续线性探测,

((8 % 7) + 2) % 7,等于3 ,但3号索引位存了3,还是冲突,继续线性探测,((8 % 7) + 3) % 7,等于4,4号索引位为空,可以存储。

 

其他两种探测法也是类似的,大家可以自己推理一下。

 

2、拉链法

基本思想:相同散列地址的记录链成一单链表,m个散列地址设置m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态结构。

bbd80a597f7f476a8e3e4be97646a837.png

 如果出现冲突,单链表就会添加一个节点。理想情况下时间复杂度可以达到O(1)。

 

七、散列表查找效率分析

ASL(平均查找长度)衡量散列表查找算法,有以下几个影响因素:

1、散列函数。

2、处理冲突的方法。

3、散列表的填装因子α。

α = 表中填入记录数 / 哈希表长度。

α越大,表示表中数据越多,发生哈希冲突的概率越大,查找时比较的次数也越多。

时间复杂度既不是O(1),也不是O(n)。

ASL ≈ 1 + α / 2                 (拉链法)

ASL ≈ (1 + 1 / (1 - α) ) / 2 (线性探测法)

 

八、宏定义

#define HASH_TABLE_MAX_SIZE  17

哈希表的最大长度,也是除留余数法中的除数。

 

九、结构体定义

 

1、HashTabElemType

typedef long long int HashTabElemType; //-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807

哈希表中存储的数据类型。

 

2、HashNode

typedef struct HashNode
{
    HashTabElemType  Data;
    struct HashNode* NextPtr;
}HashNode;

哈希表中单链表的结构。

 

3、HashTable

typedef struct HashTable
{
    HashNode*       FirstHashNode;
    HashTabElemType DataNum;
}HashTable;

哈希表的结构。DataNum表示这条链上有多少个数据节点。

 

十、函数定义

 

1、初始化哈希表

Status InitHashTable(HashTable** HashTab)
{
    JudgeAllNullPointer(HashTab);
    *HashTab = (HashTable*)MyCalloc(HASH_TABLE_MAX_SIZE, sizeof(HashTable));
    Log("Init Hash Table OK\n",Debug);
    return SuccessFlag;
}

 

 2、哈希函数(除留余数法)

//除留余数法
HashTabElemType MyHashFunc(HashTabElemType Num)
{
    return Num % HASH_TABLE_MAX_SIZE;
}

 

 3、添加节点

HashNode* NewHashNode(HashTabElemType Num)
{
    HashNode* NewNode = (HashNode*)MyMalloc(sizeof(HashNode));
    NewNode->Data     = Num;
    NewNode->NextPtr  = NULL;
    return NewNode;
}

 

 4、插入数据到哈希表

Status InsertHashTable(HashTable* HashTab, HashTabElemType Num)
{
    JudgeAllNullPointer(HashTab);

    HashTabElemType HashValue = MyHashFunc(Num);

    (HashTab[HashValue].DataNum)++;

    if(HashTab[HashValue].FirstHashNode == NULL)//no data
    {
        HashTab[HashValue].FirstHashNode = NewHashNode(Num);
        Log("Insert Hash Table OK\n",Debug);
        return SuccessFlag;
    }

    //have data
    HashNode* NewNode                = NewHashNode(Num);
    NewNode->NextPtr                 = HashTab[HashValue].FirstHashNode;
    HashTab[HashValue].FirstHashNode = NewNode;

    Log("Insert Hash Table OK , Hash Conflict\n",Debug);
    return SuccessFlag;
}

实现思路:

1、算哈希值,定索引位,此链表数据长度加一。

2、如果链表头指针为空,就插入。

3、如果不为空,就用头插法把数据插入到链表中。

 

 5、哈希表搜索值

//HashValue为返回的索引位
Status SearchHashTable(HashTable* HashTab, HashTabElemType Num, HashTabElemType* HashValue)
{
    JudgeAllNullPointer(HashTab);

    *HashValue = MyHashFunc(Num);

    HashNode* TmpHashNode = HashTab[*HashValue].FirstHashNode;
    while(TmpHashNode != NULL)
    {
        if(TmpHashNode->Data == Num)
        {
            Log("Search Hash Table OK\n",Debug);
            return SuccessFlag;
        }
        TmpHashNode = TmpHashNode->NextPtr;
    }

    Log("Search Hash Table Fail\n",Debug);
    return FailFlag;
}

实现思路:

1、算哈希值,将值赋予输出参数HashValue,定索引位。

2、如果链表指针为空,查询失败。

3、如果匹配到值,查询成功。

 

十一、Linux环境测试

[gbase@localhost Select]$ make
gcc -Wall -O3 ../Log/Log.c ../PublicFunction/PublicFunction.c Select.c HashTable.c main.c -o TestSelect -I ../Log/ -I ../PublicFunction/ -I ./
[gbase@localhost Select]$ ./TestSelect 
[2023-4]--[ Debug ]--Init Hash Table OK
[2023-4]--[ Debug ]--Insert Hash Table OK
[2023-4]--[ Debug ]--Insert Hash Table OK , Hash Conflict
[2023-4]--[ Debug ]--Insert Hash Table OK
[2023-4]--[ Debug ]--Insert Hash Table OK
[2023-4]--[ Debug ]--Printf HashTable
DataNum : 0     , [(nil)]
DataNum : 2     , [1,1]
DataNum : 1     , [2]
DataNum : 1     , [3]
DataNum : 0     , [(nil)]
DataNum : 0     , [(nil)]
DataNum : 0     , [(nil)]
DataNum : 0     , [(nil)]
DataNum : 0     , [(nil)]
DataNum : 0     , [(nil)]
DataNum : 0     , [(nil)]
DataNum : 0     , [(nil)]
DataNum : 0     , [(nil)]
DataNum : 0     , [(nil)]
DataNum : 0     , [(nil)]
DataNum : 0     , [(nil)]
DataNum : 0     , [(nil)]
[2023-4]--[ Debug ]--Search Hash Table OK
1
[2023-4]--[ Debug ]--Search Hash Table Fail
8

 

 

 

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

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

相关文章

【微服务笔记16】微服务组件之Gateway服务网关基础环境搭建、高可用网关环境搭建

这篇文章&#xff0c;主要介绍微服务组件之Gateway服务网关基础环境搭建、高可用网关环境搭建。 目录 一、Gateway服务网关 1.1、什么是Gateway 1.2、Gateway基础环境搭建 &#xff08;1&#xff09;基础环境介绍 &#xff08;2&#xff09;引入依赖 &#xff08;3&#…

快速上手Navicat~

众所周知&#xff0c; Navicat是一款轻量级的用于MySQL连接和管理的工具&#xff0c;非常好用&#xff0c;使用起来方便快捷&#xff0c;简洁。下面我会简单的讲一下其安装以及使用的方法。并且会附带相关的永久安装教程。 简介 一般我们在开发过程中是离不开数据库的&#xf…

【Unity VR开发】结合VRTK4.0:添加对象追随器

语录&#xff1a; 我已经准备好了足够挡雨的伞&#xff0c;可是却迟迟没有等到雨的到来&#xff0c;这样的尴尬只是我漫长人生中的小插曲罢了。 前言&#xff1a; 对象追随器的目的是让一个或多个游戏对象跟随场景中的另一个对象&#xff0c;而无需将游戏对象嵌套在彼此之下。 …

『pyqt5 从0基础开始项目实战』13. 打包生成exe(保姆级图文)

目录 项目源码打包exe打开闪退需要db文件夹总结 欢迎关注 『pyqt5 从0基础开始项目实战』 专栏&#xff0c;持续更新中 欢迎关注 『pyqt5 从0基础开始项目实战』 专栏&#xff0c;持续更新中 项目源码 请查阅专栏上文获取源码 ## 安装库包 python pip install pyinstaller ![…

Stable Diffusion的原理

CSDN-markdown语法之怎样使用LaTeX语法编写数学公式 参考视频&#xff1a;【diffusion】扩散模型详解&#xff01;原理代码&#xff01; 用一颗桃树为你讲清楚 知识点&#xff1a;AI绘图原理 Diffusion扩散模型 Windows深度学习环境搭建&#xff1a;Windows深度学习环境搭建 …

FFmpeg开发笔记(三)FFmpeg的可执行程序介绍

外界对于FFmpeg主要有两种使用途径&#xff0c;一种是在命令行运行FFmpeg的可执行程序&#xff0c;该方式适合没什么特殊要求的普通场景&#xff1b;另一种是通过代码调用FFmpeg的动态链接库&#xff0c;由于开发者可以在C代码中编排个性化的逻辑&#xff0c;因此该方式适合厂商…

一篇文章介绍分布式事务

1、事务的基本概念 事务 事务指的就是一个操作单元&#xff0c;在这个操作单元中的所有操作最终要保持一致的行为&#xff0c;要么所有操作都成功&#xff0c;要么所有的操作都被撤销。简单地说&#xff0c;事务提供一种“要么什么都不做&#xff0c;要么做全套”机制。 本地…

【越早知道越好】的道理——能够大大提升效率的【快捷键】

文章目录 1️⃣虚拟桌面第一步&#xff1a;打开任务视图第二步&#xff1a;创建桌面第三步&#xff1a;桌面切换第四步&#xff1a;桌面删除 2️⃣窗口切换3️⃣桌面分屏如何分屏 前言&#x1f9d1;‍&#x1f3a4;&#xff1a;作为程序员&#x1f468;‍&#x1f4bb;&#xf…

scratch足球射门练习 中国电子学会图形化编程 少儿编程 scratch编程等级考试一级真题和答案解析2023年3月

目录 scratch足球射门练习 一、题目要求 1、准备工作 2、功能实现 二、案例分析

基于Java+SpringBoot+Vue前后端分离仓库管理系统设计实现

基于JavaSpringBootVue前后端分离仓库管理系统设计实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式…

RocketMQ 5.0 时代,6 张图带你理解 Proxy!

大家好&#xff0c;我是君哥。今天来聊一聊 RocketMQ 5.0 中的 Proxy。 RocketMQ 5.0 为了更好地拥抱云原生&#xff0c;引入了无状态的 Proxy 模块&#xff0c;新的架构图如下&#xff1a; 引入 Proxy 模块后&#xff0c;Proxy 承担了协议适配、权限管理、消息管理等计算功能…

JVM垃圾回收GC 详解(java1.8)

目录 垃圾判断算法&#xff08;你是不是垃圾&#xff1f;&#xff09; 引用计数法 可达性算法 对象的引用 强引用 软引用 弱引用 虚引用 对象的自我救赎 垃圾回收算法--分代 标记清除算法 复制算法 标记整理法 垃圾处理器 垃圾判断算法&#xff08;你是不是垃圾&…

Anaconda+PyTorch环境搭建

AnacondaPyTorch环境搭建 环境Anaconda安装配置下载镜像 cuda和cudnncudacudnn pytorch参考文章 环境 win10 22hx nvidia driver 528.89 Anaconda安装 在清华镜像中选择合适的版本及对应系统 https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 下载好之后一路next即可…

vscode笔记

vscode怎么中止运行 控制台—输出&#xff0c;结束运行&#xff1a; 控制台右击&#xff0c;然后点击Stop Code Run或者CtrlAltM快捷键 停止正在运行的Python脚本 在VS Code的右下角&#xff0c;选择正在运行的终端&#xff0c; 点右键&#xff0c;终止终端 VS code怎么终止…

简单的小型C++项目怎么用CMAKE进行管理

项目目录&#xff1a; 根目录下共有两个文件夹&#xff0c;分别为include、src&#xff0c;有两个文件&#xff0c;分别为CMakeLists.txt和main.cpp main函数 可以看出&#xff0c;include了func.h&#xff0c;且func.h的声明在include文件夹下&#xff0c;定义在src文件夹下的…

全网唯一!Matlab世界顶尖艺术品配色包Rmetbrewer

想要绘制一幅颜色搭配合理、好看又不花哨的论文插图&#xff0c;该如何操作呢&#xff1f; 正所谓求其上者得其中&#xff0c;求其中者得其下。 那么&#xff0c;向高手借鉴思路&#xff0c;无疑是一种不落下乘的好策略。 而在色彩搭配领域&#xff0c;像莫奈、梵高这些世界…

WPS表格数据出现绿色小三角,单引号,E+的原因说明和完美解决方案,终结版。

复盘问题的原因&#xff0c;了解原因的原因&#xff0c;预测事件的结果&#xff0c;推测结果产生的结果。 问题描述&#xff1a;好好的数据&#xff0c;复制进wps&#xff0c;左上角就会出现绿色三角。怎么去掉呢&#xff1f; 迷惑问题2&#xff1a;点击之后&#xff0c;绿色三…

代码优化- 前端优化

常量折叠 基本思想&#xff1a;在编译期间计算表达式的值&#xff08;编译时静态计算&#xff09; 例如&#xff1a;a 3 5 > a 8&#xff0c;if (true && false) ... > if (false) 好处是&#xff1a;语法树的节点数量减少了&#xff0c;意味着编译器要维护…

STM32—0.96寸OLED液晶显示

本文主要介绍基于STM32F103的0.96寸的OLED液晶显示&#xff0c;详细关于0.96寸OLED液晶屏幕的介绍可参考这篇博客&#xff1a;https://blog.csdn.net/u011816009/article/details/130119426 一、简介 OLED被称为有机激光二极管&#xff0c;也被称为有机激光显示&#xff0c;O…

学生宿舍管理系统【GUI/Swing+MySQL】(Java课设)

系统类型 Swing窗口类型Mysql数据库存储数据 使用范围 适合作为Java课设&#xff01;&#xff01;&#xff01; 部署环境 jdk1.8Mysql8.0Idea或eclipsejdbc 运行效果 本系统源码地址&#xff1a;https://download.csdn.net/download/qq_50954361/87700423 更多系统资源库…