Acwing Hash表

news2024/9/21 17:02:06

哈希表的作用:把一个比较大的空间,通过一个函数映射到一个比较小的空间
一般做哈希运算时,取一个质数作为模,会使得冲突的概率降低。
哈希表的冲突解决方法:

  • 拉链法
  • 开放寻址法

下面详细介绍这两种方法的原理及其实现:

1.拉链法

创建一个数组h[],插入一个值时通过哈希函数映射到数组对应位置,每个位置维护一个链表,映射到相同位置加入当前链表中。
数组h[i]类似于链表的头指针,存储的是其下链表的第一个结点x的数组下标**,而不是结点的值x,取值范围0~N,所以可以让数组h的默认值为-1以此判断该位置下是否为空

  • 插入操作:采用头插法,根据哈希函数计算哈希值,每次冲突的值,插入到链表的第一个位置;
  • 查询操作:根据哈希值找到对应头指针即对应链表,再对链表遍历判断;
  • 删除操作:删除操作并不是真正的删除该元素,而是设置一个标记值,来表示该位置的值已被删除(用得少)。
    板子:
const int N = 100003; // 大于10^5的最小质数,作为哈希表的大小

int h[N], e[N], ne[N], idx = 0; 
// h[] 用于存储每个哈希值对应的链表头结点
// e[] 用于存储插入的值
// ne[] 用于存储每个元素的下一个节点的索引
// idx 是当前插入元素的索引

// 插入操作,使用拉链法实现
void insert(int x){
    int k = (x % N + N) % N; // 计算哈希值,处理负数时确保结果为正数
    e[idx] = x;              // 将x存入数组e,idx是当前存储的索引
    ne[idx] = h[k];          // 将当前元素的下一个节点设置为链表的第一个节点
    h[k] = idx++;            // 将哈希表h[k]指向当前元素,并更新idx
}

// 查找操作,查找元素x是否存在
bool find(int x){
    int k = (x % N + N) % N; // 计算哈希值
    for(int i = h[k]; i != -1; i = ne[i]){ // 遍历哈希表k对应的链表
        if(e[i] == x) return true;         // 如果找到,返回true
    }
    return false;                          // 没找到,返回false
}

哈希表常用取余法,代码实现找一个大于等于N且最小的质数:

int get_Prime(int N){
    for(int i = N;;i++){
       bool flag = true;
       for(int j = 2; j * j <= i;j ++){
          if(i % j== 0){
             flag = false;
             break;
          }
       }
       if(flag){
           return i;
           break;
       }
    }
}

2.开放寻址法:

开放寻址法:当冲突发生时,使用某种探测算法(得出一个偏移量)在散列表中寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到。探测法有线性探测法、平方探测法、双散列法、伪随机序列。这里直接使用线性探测法,即冲突则自增1。

数组h[]存储的时具体的节点值x,而x的取值范围是 − 1 0 9 − − 1 0 9 -10^9 --10^9 109109.故应该让数组x的默认值不在x的取值范围内(定义null = 0x3f3f3f3f),这样才好判断h[k[位置上是否为空(注意和拉链法区分)

  • 查找和查询操作合为一个find()函数:首先根据哈希函数计算的哈希值查找当前元素是否在初始位置。若该位置为空,则在这个位置插入该元素;若不为空且与该元素不等,则向后继续查找,直到找到该元素或有空位置则插入该元素。最后返回该位置。

板子:

const int N = 200003, null = 0x3f3f3f3f; // N为哈希表的大小,null为标志无效值的常量
int h[N]; // 哈希表,存储元素

// 开放寻址法查找函数
// 如果x在哈希表中,返回x的下标;
// 如果x不在哈希表中,返回x应该插入的位置
int find(int x) {
    int k = (x % N + N) % N; // 计算哈希值,处理负数使其为正
    while(h[k] != null && h[k] != x) { // 当位置不为空且不等于x时,进行线性探测
        k++; // 如果发生冲突,继续探测下一个位置
        if(k == N) k = 0; // 如果到达末尾,回到哈希表开头
    }
    return k; // 返回找到的插入位置或x所在的位置
}

好的,下面给出一道题目,我们采用两种方法解答:

Acwing 840. 模拟散列表
在这里插入图片描述
输入样例:

5
I 1
I 2
I 3
Q 2
Q 5

输出样例:

Yes
No

具体实现(2种):

//拉链法
#include <iostream>
#include <cstring>

using namespace std;

const int N = 100003; // 大于10^5的最小质数,作为哈希表的大小

int h[N], e[N], ne[N], idx = 0; 
// h[] 用于存储每个哈希值对应的链表头结点
// e[] 用于存储插入的值
// ne[] 用于存储每个元素的下一个节点的索引
// idx 是当前插入元素的索引

// 插入操作,使用拉链法实现
void insert(int x){
    int k = (x % N + N) % N; // 计算哈希值,处理负数时确保结果为正数
    e[idx] = x;              // 将x存入数组e,idx是当前存储的索引
    ne[idx] = h[k];          // 将当前元素的下一个节点设置为链表的第一个节点
    h[k] = idx++;            // 将哈希表h[k]指向当前元素,并更新idx
}

// 查找操作,查找元素x是否存在
bool find(int x){
    int k = (x % N + N) % N; // 计算哈希值
    for(int i = h[k]; i != -1; i = ne[i]){ // 遍历哈希表k对应的链表
        if(e[i] == x) return true;         // 如果找到,返回true
    }
    return false;                          // 没找到,返回false
}

int main(){
    memset(h, -1, sizeof h); // 初始化哈希表h,令所有值为-1,表示链表为空
    int n; // 操作数
    cin >> n;
    while(n--){ // 处理n个操作
        char op[2]; // 操作符
        int x;      // 操作的数值
        cin >> op >> x;
        if(op[0] == 'I') insert(x); // 插入操作
        else { // 查找操作
            if(find(x)) puts("Yes"); 
            else puts("No");         
        }
    }
    return 0;
}

//开放寻址法
#include <iostream>
#include <cstring>

using namespace std;

const int N = 200003, null = 0x3f3f3f3f; // N为哈希表的大小,null为标志无效值的常量
int h[N]; // 哈希表,存储元素

// 开放寻址法查找函数
// 如果x在哈希表中,返回x的下标;
// 如果x不在哈希表中,返回x应该插入的位置
int find(int x) {
    int k = (x % N + N) % N; // 计算哈希值,处理负数使其为正
    while(h[k] != null && h[k] != x) { // 当位置不为空且不等于x时,进行线性探测
        k++; // 如果发生冲突,继续探测下一个位置
        if(k == N) k = 0; // 如果到达末尾,回到哈希表开头
    }
    return k; // 返回找到的插入位置或x所在的位置
}

int main() {
    //memset按字节赋值,int4个字节,每个字节赋值0x3f,则h默认值就为0x3f3f3f3f 即null
    memset(h, 0x3f, sizeof h); // 初始化哈希表,所有位置都标记为null
    int n; // 操作次数
    cin >> n;
    while(n--) {
        char op[2]; // 操作类型
        int x; // 操作的数值
        cin >> op >> x;
        int k = find(x); // 通过find函数得到x的位置
        if(op[0] == 'I') h[k] = x; // 插入操作,将x放入找到的位置
        else {
            if(h[k] != null) puts("Yes"); // 如果h[k]不是null,说明x在哈希表中
            else puts("No"); // 否则x不在哈希表中
        }
    }
    return 0;
}

以上两种方法各有利弊,开放寻址法通过线性探测有效解决哈希冲突,适合负载率较低的哈希表。在实际操作中,开放寻址法避免了链表(拉链法)的额外存储开销,但在负载率高时可能会导致较多的探测,影响性能。个人倾向于开放寻址法,只需要开一个数组~

下面还有个无敌的算法:字符串哈希

可以求解任意的字串的哈希值!这是KMP望而却步的!可以通过字符串哈希值,快速判断两个字符串是否相等或者两个字符串中某个部分是否相等。(用模式匹配至少 O ( n ) O(n) O(n),而字符串哈希只需要 O ( 1 ) . O(1). O(1).

  1. 字符串哈希值:实际为字符串前缀哈希值,如有字符串s = ABCDE,用数组h[]存储各个前缀哈希值,则h[1] = A的哈希值;h[2] =AB的哈希值;h[3] = ABC的哈希值…
  2. 如何求解一个字符串的哈希值:将字符串看成一个P禁进制的数,比如字符串ABCD,假设我们把A映射为1,B映射为2,C映射为3,D映射为4(实际上字母也直接取它的ASCII值也可)。将ABCD这个P进制数转化为10进制即为哈希值,则ABCD这个字符串的哈希值为: ( 1 ∗ P 3 (1* P^3 (1P3+ 2 ∗ P 2 2* P^2 2P2+ 3 ∗ P 1 3*P^1 3P1+ 4 ∗ P 0 ) 4*P^0) 4P0) m o d Q mod Q modQ. 最后mod Q 即防止转化的十进制数过大,用Q来缩小数据范围,就是哈希散列的意义;
    –注意通常不要把一个字母映射为0,这样会导致重复。比如把A映射为0,则A也是0,AA也是0,AAA还是0;
    –对于P、Q的取值,有一个经验值,将冲突概率降到极低。我们可以取 P = 131或13331,Q = 2 64 2^{64} 264 。为简化mod Q运算,可以将h数组的类型取成unsigned long long(64位),这样就无需对 2 64 2^{64} 264取模,溢出就直接相当于取模
    在这里插入图片描述

可得一个递推公式,求解某个字符串s[]的(前缀)哈希值:即h[i]=h[i-1]*P+s[i](类似求前缀和,只是这里每次要乘一个P)注意:i从1开始;字符串是顺序存储在数组中,低位字符的权值大

  1. 求解一个字符串区间[l,r]上的字符串哈希值
    在这里插入图片描述

这就和前缀和有区别,不是单纯的h[r] - h[l-1].因为字符串是顺序存储在数组中,低位字符的权重大,区间[1,l-1]的字符串在相减过程中后面应该补0,即抬高各字符的权重,达到与[1,r]区间的字符串位数相等,有同等地位。如[1,l-1]是“ABC”,而[1,r]是“ABCDE”,求DE的哈希值,就应该让哈希值(ABCDE)- 哈希值(ABC00)
即体现到公式上,先算出两者相差的位数:r-l+1,然后h[l-1]*P^(r-l+1),再相减得[l,r]区间字符串的哈希值=h[r]-h[l-1]*P^(r-l+1)
注:为了简便的得到P^i的值,用一个数组来存储,即p[i]=P^i

  1. 由以上步骤,即可通过哈希值来判断任意两个字符串是否相等

具体的题目为:Acwing 841 字符串哈希

在这里插入图片描述
输入样例:

8 3
aabbaabb
1 3 5 7
1 3 6 8
1 2 1 2

输出样例:

Yes
No
Yes

下面直接给出具体实现代码(详解版)

#include <iostream>
#include <string>

using namespace std;

const int N = 1e5 + 10, p = 131;  // N 为最大字符串长度,p 是哈希基数,用于计算哈希值
typedef unsigned long long ULL;   // 使用无符号长整型存储哈希值,避免溢出问题
int n, m;                         // n 是字符串长度,m 是查询次数
int h[N], P[N];                   // h[] 存储前缀哈希值,P[] 存储 p 的幂
char str[N];                      // 字符串,索引从 1 开始

// 得到区间 [l, r] 的字符串哈希值
ULL get(int l, int r) {
    // 利用前缀哈希公式:哈希值为 h[r] - h[l-1] * p^(r-l+1)
    return h[r] - h[l-1] * P[r-l+1];
}

int main() {
    cin >> n >> m >> str + 1;  // 输入字符串长度 n、查询次数 m、字符串 str(索引从 1 开始)
    
    P[0] = 1;  // p^0 = 1
    for (int i = 1; i <= n; i++) {
        P[i] = P[i - 1] * p;  // 计算 p^i,用于后续哈希值的计算
        h[i] = h[i - 1] * p + str[i];  // 计算前缀哈希值
    }
    
    while (m--) {
        int l1, r1, l2, r2;  // 查询两个子串的区间 [l1, r1] 和 [l2, r2]
        cin >> l1 >> r1 >> l2 >> r2;
        
        // 如果两个区间的哈希值相等,说明两个子串相等
        if (get(l1, r1) == get(l2, r2)) puts("Yes");
        else puts("No");
    }
    
    return 0;
}

以上就是哈希的一些知识点,还是很好用的~

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

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

相关文章

Java+Spring Cloud +UniApp 智慧工地源码,用户PC端、移动端数据同步,支持多端展示

数字化给各行各业所带来的改变&#xff0c;在早些年间突出自动这一流程。但随着科技的发展&#xff0c;让人们也愈发了解可视化操作所带来的优势。智慧工地的诞生&#xff0c;相当于为建筑施工带来了一套较为完整的数字化流程&#xff0c;能够完善施工环节中的各部分内容。接下…

C++【类和对象】(一)

文章目录 前言1.类的定义1.1类定义格式1.2 访问限定符1.3 类域 2. 实例化2.1 实例化的概念2.2 对象大小 3.this指针结语 前言 在前文我们讲解了C基础语法知识。本文将会讲解C的类和对象。 1.类的定义 1.1类定义格式 class name {}&#xff1b;class为定义类的关键字&#x…

8586 括号匹配检验

### 思路 1. **初始化栈**&#xff1a;创建一个空栈用于存储左括号。 2. **遍历字符串**&#xff1a;逐个字符检查&#xff1a; - 如果是左括号&#xff08;( 或 [&#xff09;&#xff0c;则入栈。 - 如果是右括号&#xff08;) 或 ]&#xff09;&#xff0c;则检查栈是…

【医学半监督】对比互补掩蔽的自监督预训练半监督心脏图像分割

SELF-SUPERVISED PRE-TRAINING BASED ON CONTRASTIVE COMPLEMENTARY MASKING FOR SEMI-SUPERVISED CARDIAC IMAGE SEGMENTATION 2024 IEEE International Symposium on Biomedical Imaging (ISBI) 摘要: 心脏结构分割对心脏病诊断非常重要,而使用大量注释的深度学习在这项任…

记录|C#的资源路径设置的资料整理

目录 前言一、在这里插入图片描述 https://bbs.csdn.net/topics/360001606 二、三、添加到资源文件中四、获得图片的三种路径方法五、给资源文件添加文件夹更新时间 前言 参考文章&#xff1a; 原本以为C# winform中进行图片等文件的路径的读取是直接可以按照资源文件中显示的来…

AI对汽车行业的冲击和比亚迪新能源汽车市场占比

人工智能&#xff08;AI&#xff09;对汽车行业的冲击正在迅速改变该行业的面貌&#xff0c;从智能驾驶到生产自动化&#xff0c;再到个性化的消费者体验&#xff0c;AI带来的技术革新在各个层面影响着汽车产业。与此同时&#xff0c;新能源汽车市场&#xff0c;特别是以比亚迪…

剑指offer JZ54 二叉搜索树的第k个节点

描述&#xff1a; 给定一棵结点数为n 二叉搜索树&#xff0c;请找出其中的第 k 小的TreeNode结点值。 1.返回第k小的节点值即可 2.不能查找的情况&#xff0c;如二叉树为空&#xff0c;则返回-1&#xff0c;或者k大于n等等&#xff0c;也返回-1 3.保证n个节点的值不一样 如…

2024 研究生数学建模竞赛(B题)建模秘籍|WLAN组网中网络吞吐量建模|文章代码思路大全

铛铛&#xff01;小秘籍来咯&#xff01; 小秘籍团队独辟蹊径&#xff0c;运用特征重要性分析&#xff0c;自适应回归&#xff0c;机器学习算法等强大工具&#xff0c;构建了这一题的详细解答哦&#xff01; 为大家量身打造创新解决方案。小秘籍团队&#xff0c;始终引领着建模…

使用丹摩DAMODEL平台进行ChatGLM-6B-部署与使用

实例创建 1.先进入官网https://doc.damodel.com 进行注册&#xff0c;新用户可以免费送代金券&#xff0c;实名注册后一共可以得到50块钱&#xff0c;现在4090还搞活动&#xff0c;1.5左右一个钟。 2.创建后进入DAMODEL控制台创建实例&#xff1a; 如下&#xff1a; 这里创建…

在vue中:style 的几种使用方式

在日常开发中:style的使用也是比较常见的&#xff1a; 亲测有效 1.最通用的写法 <p :style"{fontFamily:arr.conFontFamily,color:arr.conFontColor,backgroundColor:arr.conBgColor}">{{con.title}}</p> 2.三元表达式 <a :style"{height:…

Hive企业级调优[4]——HQL语法优化之分组聚合优化

HQL语法优化之分组聚合优化 优化说明 在 Hive 中&#xff0c;未经优化的分组聚合通常通过一个 MapReduce Job 实现。Map 端负责读取数据&#xff0c;并按分组字段进行分区&#xff0c;通过 Shuffle 将数据发送至 Reduce 端&#xff0c;在 Reduce 端完成最终的聚合运算。 Hiv…

进程和线程问题解答

线程和进程的概念、区别 进程是操作系统进行资源分配的基本单位&#xff0c;拥有独立的地址空间&#xff0c;包括代码、数据、堆、栈等。进程间的切换开销较大。 线程是进程中的一个执行单元&#xff0c;是系统中最小的执行单位&#xff0c;共享进程的资源&#xff0c;如代码…

利士策分享,中日核污染水排海问题共识背后的深思

利士策分享&#xff0c;中日核污染水排海问题共识背后的深思 近日&#xff0c;中日两国在应对福岛第一核电站核污染水排放问题上达成了重要共识&#xff0c;这一进展无疑值得肯定。 然而&#xff0c;这背后所引发的关于金钱、责任与利益的讨论&#xff0c;却值得我们深入探究。…

使用宝塔部署项目在win上

项目部署 注意&#xff1a; 前后端部署项目&#xff0c;需要两个域名&#xff08;二级域名&#xff0c;就是主域名结尾的域名&#xff0c;需要在主域名下添加就可以了&#xff09;&#xff0c;前端一个&#xff0c;后端一个 思路&#xff1a;访问域名就会浏览器会加载前端的代…

什么是3D展厅?有何优势?怎么制作3D展厅?

一、什么是3D展厅&#xff1f; 3D展厅是一种利用三维技术构建的虚拟展示空间。它借助虚拟现实&#xff08;VR&#xff09;、增强现实&#xff08;AR&#xff09;等现代科技手段&#xff0c;将真实的展示空间数字化&#xff0c;呈现出逼真、立体、沉浸的展示效果。通过3D展厅&a…

剪映草稿批量自动化导出教程实操演示

如何批量自动导出草稿&#xff1f;今天我来实操演示。首先打开谷歌剪映助手 如果没有安装谷哥剪映助手的可以自行搜索下载&#xff0c;打开后找到批量导出多个草稿自动化导出。接着在右侧输入你要导出草稿的数量&#xff0c;其他的选项根据需求自行选择&#xff0c;最后点击立即…

【网络安全 | 靶机搭建】解决虚拟机联网问题(NAT模式)

背景:在电脑上下载并使用VMware虚拟机后,重装VMware时可能会遇到虚拟机无法联网的问题(例如,ping www.baidu.com 无法通畅)。这种情况可能是网络适配器被删除导致的。 本文将通过添加网络、安装网络适配器以及切换网络连接方式等步骤解决虚拟机的联网问题,具体步骤如下:…

笔记:将WPF中可视化元素(Visual)保存为图像,如PNG,JPEG或BMP的方法简介

一、目的&#xff1a;将WPF中可视化元素&#xff08;Visual&#xff09;保存为图像&#xff0c;如PNG,JPEG或BMP的方法简介 BitmapEncoder 是 WPF 中用于将图像数据编码为特定格式的基类。它提供了将 BitmapSource 对象保存为各种图像格式&#xff08;如 PNG、JPEG、BMP 等&…

Android 空气质量刻度

效果 attrs.xml <attr name"textSpace" format"dimension|reference" /><attr name"barSpace" format"dimension|reference" /><attr name"scaleHeight" format"dimension|reference" /><at…

armbian debian 系统安装overlayroot后无法启用

千盼万盼 终于debian12可以用了 它终于也跟ubuntu 系统一样可以安装overlayroot了 但是 满怀欣喜的装完了发现 压根没法启动 这。。。。。 原因吗 也简单。。。 默认的映像里没有busybox......... 而它有这个要求。。。 overlayroot 包中有一个小错误&#xff1a;它要求 gr…