15-哈希表

news/2024/11/8 11:43:40/

哈希表(Hash table),也称散列表,是一个能够将数值映射而成地址从而进行直接访问的数据结构,通过哈希表我们可以快速、迅捷地访问数据。

哈希表原理

假设我们拥有一个数x(也称关键值,key),那么我们可以通过哈希函数,将这个关键值x映射成一个数k,再将这个数k对应成一个表中的位置,那么我们就可以通过这个表来访问或者存储数据了。

由此可以看出,我们想要创建一个哈希表,首先需要创建一个哈希函数。由上图可以知道,哈希函数主要功能是将一个关键值转换为一个地址,可以理解为一个多变少的过程,例如关键值x的范围假位于-10^{9}10^{9}之间,那么利用哈希的方式我们可以将这个关键值x的范围缩小到0~10^{5}。那么我们要想限制某个数取到一定的区间范围,那么我们自然而然想到取模操作。取模操作可以让限定一个数x变成比所取得模小的数。这里我们想将范围限定到1000以内。但是这里有个问题,如果我们限定关键值为范围-10^{9}10^{9},那么很大情况下多个数通过哈希函数映射出来的是一个数!

拉链法

拉链法原理

为此我们需要在每一个值k后面创建一个链表来存储这些相同映射的关键值,这种方式也称为拉链法。

虽然有方法可以解决这个问题,但是我们还是需要尽量避免这种情况的发生,为此我们取需要一个靠近1000的质数N,这里求出来是1003。得到了这个数后,我们就用x除以这个数N取模。但是还有一个问题,我们尽量让取得模是正数,所以要关注关于负数的余数问题,即还需要将其负数的模转换为正数,为此我们将其模加上N本身再取模。


using namespace std;const int N = 1003;int Hash_func(int x)
{return (x % N + N) % N;
}

哈希表插入(拉链法)

在此之后,我们就可以考虑如何插入一个数进哈希表了。首先我们需要有一个数组来存储所有我们可能映射出来的数k的集合。这里开辟一个数组h,因为我们设定最多只有1003个映射位置地址,所以这个数组也是大小为1003。那么我们存储过程就可以理解为在在这个数组下面挂上一串数据。例如我们有一个数据x,我们将其传入哈希函数后得到的位置为k,我们就找到在数组h种下标为k的位置 ,将这个数据挂在其下面,而“挂”的过程可以使用单链表来进行。

对应于代码,我们首先创建一个h数组、单链表指针数组Node、单链表数值数组val和当前下标index,然后获取地址值后在其下方创建一个节点即可,那么数组h[k]就对应单链表的头节点了。所以我们要对h数组所有值进行初始化,赋值为-1(因为单链表头节点在其它节点还没有创建的时候指向下标我们默认设置为-1)。

using namespace std;
const int N = 1003;
int h[N], Node[N],val[N],index;void Insert(int x)
{int n = Hash_func(x);val[index] = x;Node[index] = h[n];h[n] = index;index++;
}int main()
{memset(h, -1, sizeof(h));return 0;
}

哈希表查询(拉链法)

 对于哈希表的查询操作,我们可以类似参考单链表的遍历查询。首先我们还是需要将需要查的数通过查哈希函数获取其对应的地址,然后在对应的地址下面遍历全部的节点。如果有节点的数值和所需要搜索的数值相同,那么我们就返回真;如果一直都找不到合适的数值,那么我们就返回假(即没有找到)。

bool find(int x)
{int n = Hash_func(x);for(int i =h[n];i!=-1;i=Node[i]){if (val[i] == x)return true;}return false;
}

开放寻址法

除了拉链法,我们还可以寻找另一种方法来解决哈希表的冲突,那就是开放寻址法。开放寻址法不同于拉链法在数组每一个元素后创建单链表来存储关键值。它只开辟一个数组,这个数组就可用来存储关键值,那它是如何处理哈希冲突的呢?

开放寻址法原理

首先我们开辟一个很大的数组,一般比预定最大大小还要大两三倍的数组。如果有一个关键值9,它通过哈希函数后得到的数是1,但是它查询后发现k=1的位置已经有一个哈希函数计算值也为1的关键值36占据了,那么它将会向后继续找空余的位置。而它后面是哈希函数值为2的关键值54,但是第三个位置是空的,那么它就可以存储在这里了。

同理,如果我们需要在这里寻找是否有某个数x,它的哈希函数计算值为1,那么我们就先通过哈希函数求得第一个满足哈希函数的关键值的在数组中的下标,然后找到这个值,将其与要求寻找的数x进行对比,如果不是就继续往后找,寻找有无和x相等的关键值,如果找到一个位置为空,那可以判断这个要找到值x不存在了。

下面我们用代码实现一下:

首先依旧是需要写一个哈希函数,并且创建一个一维数组,这里我创建比原来大两倍。但是这里有个问题,我们应该如何判断一个格子是否为空?因为一个数组会进行初始化,里面会存一些其它的数值。为此,我们决定利用一个在关键值的有效区间外的大数作为“空”的参照物。一开始这个数会充满整个一维数组,那么此时我们就可以认为这个数字“空”了。所以我们还设置了一个变量is_Empty。

#include<iostream>
#include<cstring>using namespace std;const int N = 2003, is_Empty = 10000;
int h[N];int hash_func(int x)
{return (x % N + N) % N;
}

 下面就是需要实现一个find函数,它起到的是查询操作。这里依旧是调用哈希函数获得地址,然后就是关键的一步,通过一个while循环来判断是否存在关键值x。while循环的终止条件有两个:一是我们找到了x(h[k]==x);二是h[k]的地方为“空”(里面存的是我们预设的大数),那么此时这个x就不存在了。如果找一遍都找不到,那么我们会选择将一个合适的空位返回去(而不是返回布尔变量之类的,这么做有利于我们后边的插入操作)。

int find(int x)
{int k = hash_func(x);while (h[k] != is_Empty && h[k] != x ){k++;if (k == N) k = 0;}return k;
}

下面就是插入操作,由于我们之前设置find函数返回的是一个合适存储x的位置,所以Insert函数我们直接将这个值赋予给这个下标k的位置即可。

void Insert(int x)
{int k = find(x);h[k] = x;
}

 总的代码:

#include<iostream>
#include<cstring>using namespace std;const int N = 2003, is_Empty = 10000;
int h[N];int hash_func(int x)
{return (x % N + N) % N;
}int find(int x)
{int k = hash_func(x);while (h[k] != is_Empty && h[k] != x ){k++;if (k == N) k = 0;}return k;
}void Insert(int x)
{int k = find(x);h[k] = x;
}int main()
{for (int i = 0; i < N; i++)h[i] = is_Empty;Insert(10);return 0;
}

参考资料

参考资料1

参考资料2


http://www.ppmy.cn/news/35942.html

相关文章

【解决】bash: conda: command not found

linux系统下&#xff0c;安装了anaconda了&#xff0c;使用conda创建环境&#xff0c;结果报错&#xff1a; bash: conda: command not found这主要是还没把conda列入环境变量&#xff0c;解决方法如下&#xff1a; 1、打开文件 bashrc&#xff1a; vim ~/.bashrc2、在弹出的…

python成功实现“高配版”王者小游戏?【赠源码】

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 本游戏完整源码、素材: 点击此处跳转文末名片获取 咳咳&#xff0c;又是一款新的小游戏&#xff0c;就是大家熟悉的王者~ 来看我用python来实现高&#xff08;di&#xff09;配版的王者 是一款拿到代码运行后&#xff0c;…

python 10

一、文件的基础知识 无论何种类型的文件&#xff0c;在内存或磁盘上最终都是以二进制编码存储的。根据逻辑上编码的不同&#xff0c;可以区分为文本文件和二进制文件。 文本文件基于字符编码&#xff0c;如ASCII码、Unicode编码等。文本文件存储的是普通字符串&#xff0c;能够…

【嵌入式烧录/刷写文件】-2.1-详解Intel Hex格式文件

目录 1 什么是Intel Hex 2 Intel Hex的格式 2.1 Intel Hex的Record结构 2.1.1 “Record type记录类型”的说明 2.1.2 “Record length记录长度”的说明 2.1.3 如何计算“Checksum校验和” 2.2 Record order记录顺序 2.3 Text line terminators文本行终止符 3 Hex文件的…

C++ MVC模式

概述 C是一种流行的编程语言&#xff0c;它可以用于构建各种类型的应用程序&#xff0c;包括Web应用程序、桌面应用程序和移动应用程序。在这里&#xff0c;我将为您介绍C中的MVC模式&#xff0c;以及如何在C中实现MVC模式。 MVC&#xff08;Model-View-Controller&#xff0…

【慕伏白教程】在Vmware中安装Ubuntu流程

【慕伏白教程】在Vmware中安装Ubuntu流程一、下载官方镜像二、新建虚拟机1. 创建虚拟机2. 安装系统镜像2.1 点击 *编辑虚拟机设置*2.1 虚拟机设置三、安装系统1. 系统初始化1.1 点击 *开启此虚拟机*1.2 选择 *Try or Install Ubuntu* 并回车1.3 等待加载ing1.4 选择语言并安装1…

【第017问 Unity Physics.OverlapSphere如何检测附近玩家?】

一、背景 如何检测一个对象范围内的玩家&#xff0c;这个可以直接使用距离判定&#xff0c;物体射线检测等相关方式&#xff1b;这里采用Physics.OverlapSphere的方式来实践其过程&#xff0c;并对Physics.OverlapSphere的使用做一下记录&#xff1b; 二、Physics.OverlapSph…

趋势面模型分析实例(python)

python 趋势面模型分析实例 已知五个气象站&#xff0c;这五个站围绕着未知的0号站。下图为各个站点的x、y坐标及及其已知值 第一行为0号点x&#xff0c;y坐标 z值未知暂设为0 而后依次为1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5号点xyz值 对未知点0进行插值。…