.哈希表.

news2024/9/21 8:04:20

哈希

哈希表:将大而复杂的数据映射到紧凑的区间内。分为:①存储结构 

(离散化是特殊的哈希,之前讲的离散化是严格保序的 映射到区间上是连续递增的)


哈希不保序,这里讲的是一般的哈希

弊端:若数据的数值较大可以对其取模后再映射,但是取模后可能造成:
之前不相同的数值取模后映射到同一位置上了,所以引入:开放寻址法拉链法

①存储结构 

{


    1(开放寻址法


    {
    添加:
        开一个一维数组h[],长度一般要开到题目要求长度的2到3倍
        1'假设a取模后的值为k,那么就将a放到h[k]上
        2'若b取模后的值仍为k,那么就将b放到h[k+i]上(即向后遍历,直到找到一个没存放数值的h[k+i]为止)

注意:由于这里数组h[ ]的长度为题设要求的2~3倍,所以一定能找到一个位置满足上述1’,2’两种情况
        ...
    查找:
        如果当前h[k]存在并且h[k]==x那么就找到了,如果!=x那么就继续向下寻找
        如果当前h[k]不存在,那么就不需要向下寻找,即x不存在
    
    删除:
        不真正的删除,而是开一个bool类型的数组,将所要删除的数值标记一下
        后续在查询、添加操作时,对此值是否存在特判一下即可

题目:840. 模拟散列表 - AcWing题库

代码:

暴力+二分,样例11/16:
#include<bits/stdc++.h>

using namespace std;

const int N=1e5+50;
int f[N];

int main()
{
    int n,i=0;
    scanf("%d",&n);
    while(n--)
    {
        char op[5];
        int x;
        scanf("%s %d",op,&x);
        if(!strcmp(op,"I"))
        {
            f[i]=x;
            i++;
        }
        else
        {
                sort(f,f+i);
                int l=0,r=i-1;
                while(l<r)
                {
                    int mid=l + r +1 >> 1;
                    if(f[mid]>x) r=mid-1;
                    else l=mid;
                }
                if(f[l]==x) cout << "Yes" << endl;
                else cout << "No" << endl;
        }
    }
    return 0;
}

 哈希:

#include<iostream>
//memset()函数所在头文件
#include<cstring>
using namespace std;
int n;
const int N=2e5+3,null=0x3f3f3f3f;
int h[N];
            
//如果x在hash表中已经存在的话,就返回x在hash表中所在的位置
//如果不存在的话就返回x应该在hash表中存储的位置
int find(int x)
{
    int k=(x%N + N)%N;
    while(h[k]!=null && h[k]!=x)
        {
            k++;
            //如果k==N的话(最边缘),那么就让它从头开始寻找
            //由于N的长度为题解要求的2倍,所以一定能找到一个k
            //这个k要么是x在hash表中的位置,要么是它可以在hash表中存在的位置
            if(k==N) k=0;
        }
        return k;
                
}
            
int main()
{
    scanf("%d",&n);
                
    memset(h,null,sizeof h);
                
    while(n--)
        {
            char op[2];
            int x;
            scanf("%s %d",op,&x);
            int k=find(x);
            if(!strcmp(op,"I")) h[k]=x;//直接记录下x可以在hash表中插入的位置
            else
            {
            //如果x存在于hash表中,那么一定不等于null
            //如果不存在于hash表中,那么k所返回的值就是x可以在hash表中存在的位置
            //但是由于这一步是查询操作,所以h[k]==null(null是初始值)
                if(h[k]!=null) cout << "Yes" << endl;
                else cout << "No" << endl;
            }
        }
    return 0;
}

}

2(拉链法

{
    添加:
        开一个一维数组a[]存下所有数值,将数值a取模后的值(假设是3),
        就在数组a[3]的后面 “拉一条链” 将a链住
        如果又有数值b取模后仍等于3,那么就在数值a的后面再“拉一条链”将数值b链住
        (每个数组a[]下对应的链就是我们之前所学的单链表)

插入过程与前面链表的插入过程一致,可翻阅查看:.单链表.-CSDN博客


        ...
    查找:
        求出数值取余后对应的是数组a[]上的那一个值,再询问此a[]上的链表是否有所要求的数值
    删除:
        不真正的删除,而是开一个bool类型的数组,将所要删除的数值标记一下
        后续在查询、添加操作时,对此值是否存在特判一下即可

代码:

#include<iostream>
//memset()函数所在头文件
#include<cstring>
using namespace std;
int n;
const int N=1e5+3;
int h[N],e[N],ne[N],idx;
            
void insert(int x)
{
    //取模,映射到数组h[]上,x可能为负数,所以先模再加 之后再次取模
    int k=(x%N+N)%N;
    e[idx]=x,ne[idx]=h[k],h[k]=idx,idx++;
}
            
bool find(int x)
{
    int k=(x%N + N)%N;
    //遍历链表h[k](链表上的第一个下标)表示数组上下标为k的下一个指向
    //每次让i指向它的下一个指向(移动指针i,使其继续向下遍历)
    //i=-1时即遍历结束(空值)
    for(int i=h[k];i!=-1;i=ne[i])
        if(e[i]==x)
            return true;
                   
    return false;
}
            
int main()
{
    scanf("%d",&n);
                
    memset(h,-1,sizeof h);
                
    while(n--)
    {
        char op[2];
        int x;
        scanf("%s %d",op,&x);
        if(!strcmp(op,"I")) insert(x);
        else
        {
            if(find(x)) cout << "Yes" << endl;
            else cout << "No" << endl;
        }
    }
return 0;
}
    

}

取模的值:

上述的N分别取的是N=2e5+3、1e5+3这里不像之前取1e5+10是因为:N是取模的值

取模的值一般满足以下规律使映射时的重复率最低:(取模的数值一般取成质数并且离2的整次幂要远的多)

计算方法:
(取模的数值一般取成质数并且离2的整次幂要远的多)
{
    //本题的操作次数不大于100000,所以从100000开始遍历(根据题设要求来初始化i)
    for(int i=100000;;i++)
    {
        bool flag=true;
        for(int j=2;j*j<=i;j++)
           if(i%j==0)
           {
               flag=false;
               break;
           }
        if(flag)
        {
            cout << i << endl;
            break;
        }
        
    }
    return 0;
    //这里输出的i就是接下来要取模的数值
}

②字符串哈希方式

字符串hash方式
    这里讲的是特殊的hash方式:字符串前缀哈希法
    类似于前缀和:
    {
    str="abcacwing",str[0]=0(特殊处理),str[1]="a",str[2]="ab",
    str[3]="abc",str[4]="abca"...
    定义某一个哈希的前缀和:把这个字符串看成是p进制的数
    每一位字母(str[i])看成是在p进制下的数字
    如果字符串str[4]="abcd",分别在p进制下,a:1,b:2,c:3,d:4

    那么str[4]在p进制下的和即为:1*p^3+2*p^2+3*p^1+4*p^0,
    这样处理就可以把字符串转化成一个数字
    但这个数字可能较大,所以mod(%)上一个数Q,那么就可以把这个大的数字映射成1~Q-1之内的数字了。

接下来通过比较数值大小即可比较两个字串是否相同。

注意:①:不能映射成0,因为0在任何进制下的值都为0,那么字符串:aasxas,axssx,cbdhd都会被映射成0
    ②:这里是假设不存在冲突,不存在重复的情况。当p=131或者13331,Q=2^64时,基本上不存在冲突
    若要模上Q(2^64)那么可以直接用unsigned long long 来存储hash值,溢出的值就是模上2^64的值。

}

这样处理的好处就是可以利用前缀哈希,来确定任意字串的哈希值

{
    这里的左边(1)是高位,右边(R)是低位(进制情况下最左边为最低位,即最左边*P^0)。所以在h[R]中R就是第0位,1就是第R-1位,h[l-1]中l-1就是第0位,1就是第l-2位。

那么如果要求L到R之间字串的hash值,那么就先要将h[l-1]的0位与h[R]的第0位对齐
这里R在l的右边,所以改变h[l-1]的0位与h[R]的第0位对齐
(不对齐0位,由于不同数位对应不同P的幂,那将导致相减毫无意义)
 比如:1*p^3+2*p^2+3*p^1+4*p^0,数位a对应的是P^3,b对应的是P^2..                                          即将h[l-1]*((P^(R-1))/(P^(l-2))

}

也就是达到数位对齐的目的字符串ABCDE 与 ABC 的前三个字符值是一样,只差两位,
乘上P的二次方把 ABC 变为 ABC00,再用 ABCDE - ABC00 得到 DE 的哈希值。
   

最终所推得的公式:


子串的hash值为①:h[R]-h[l-1](P^(R-L+1));

所以最终任意字符串的hash值为②:h[i]=h[i-1]*P + str[i];

公式①:

公式②:

题目:841. 字符串哈希 - AcWing题库

代码:

暴力+双指针,样例:8/13:
#include<iostream>

using namespace std;

int main()
{
    int m,n;
    scanf("%d %d",&m,&n);
    char str[100050];
    scanf("%s",str);
    while(n--)
    {
        int x1,y1,x2,y2;
        scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
        bool flag=true;
        while(x1<=y1 && x2<=y2)
        {
            if(str[x1-1]!=str[x2-1] ||str[y1-1] != str[y2-1])
            {
                flag=false;
                break;
            }
            else x1++,x2++,y1--,y2--;
        }
        if(flag) printf("Yes\n");
        else printf("No\n");
    }
}

 哈希字符串:

#include<bits/stdc++.h>

using namespace std;

typedef unsigned long long ULL;
const int N=1e5+10,P=131;

int n,m;
char str[N];
//p[]是对进制P的预处理,记录了P的不同幂次方
//h[]存放的是该字符串的每一个字母对应的hash值
ULL h[N],p[N];

int get(int l,int r)
{
    return h[r]-h[l-1]*p[r-l+1];
}
int main()
{
    scanf("%d%d%s",&n,&m,str+1);
    
    p[0]=1;
    for(int i=1;i<=n;i++)
    {
//这里需要多次运用到P的幂次方,所以在此提前做预处理
        //预处理,这里溢出值相当于取模后的值
        p[i]=p[i-1]*P;
        //转换成P进制下的数
        h[i]=h[i-1]*P+str[i];
    }
    
    while(m--)
    {
        int l1,r1,l2,r2;
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        
        if(get(l1,r1)==get(l2,r2)) printf("Yes\n");
        else printf("No\n");
    }
    
    return 0;
    
}

tips:

注意上述所给出的一些小技巧,能更好的帮助理解。

而且比如:p[i]=p[i-1]*P;很好的避免了pow(P,i)的繁杂操作

倘若还是不理解就先将公式记下来

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

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

相关文章

【Spring】认识 Spring AOP

认识 Spring AOP 1.什么是 AOP2.AOP 中的概念3.用 AOP 方式管理日志3.1 编写 AOP 日志注解类3.2 编写控制器用于测试 1.什么是 AOP AOP&#xff08;Aspect Oriented Program&#xff0c;面向切面编程&#xff09;把业务功能分为核心、非核心两部分。 核心业务功能&#xff1a…

Spark-RDD-依赖关系详解

Spark概述 Spark-RDD概述 Spark-RDD-依赖关系 在Apache Spark中&#xff0c;RDD&#xff08;Resilient Distributed Dataset&#xff09;是一种基本的抽象数据结构&#xff0c;代表了分布式的、不可变的数据集。 RDD之间的依赖关系在Spark中非常重要&#xff0c;因为它们决定了…

YOLO 学习和使用 (重拾机器学习)

contents a nenrons 单层神经网络 多层神经网络 CNN (Convolutional Neural Network) YOLO 5.1. YOLO(you only look once) 5.2. predict stage: 置信度 * 类别条件概率 全概率非极大值抑制&#xff0c;通过IOU 指数进行实现每个 grid cell 生成两个预测 bounding box 无…

4. C++网络编程-TCP客户端的实现

TCP Client网络编程基本步骤 创建socket&#xff0c;指定使用TCP协议使用connect连接服务器使用recv/send接收/发送数据关闭socket TCP-connect连接请求 !man 2 connect #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int connect(int sock…

Java面试题--JVM大厂篇(1-10)

引言&#xff1a; 在这个信息时代&#xff0c;对于准备进入大厂工作的朋友们来说&#xff0c;对于JVM&#xff08;Java虚拟机&#xff09;的掌握是面试中的一项重要内容。下面是一些精选的JVM面试题&#xff0c;希望对大家能有所帮助。 正文&#xff1a; 1. JVM有哪几种垃圾收…

车道线识别与预警系统LDWS(代码+教程)

车道线识别与预警系统&#xff08;Lane Departure Warning System, LDWS&#xff09;作为智能交通系统中的重要组成部分&#xff0c;旨在通过先进的图像处理和计算机视觉技术&#xff0c;实时监测车辆行驶过程中的车道位置&#xff0c;预防因驾驶员疏忽或疲劳导致的车道偏离事故…

自己手写了一个大模型RAG项目-06.使用自己的embedding模型

大家好&#xff0c;我是程序锅。 github上的代码封装程度高&#xff0c;不利于小白学习入门。 常规的大模型RAG框架有langchain等&#xff0c;但是langchain等框架源码理解困难&#xff0c;debug源码上手难度大。 因此&#xff0c;我写了一个人人都能看懂、人人都能修改的大…

Unity入门理论+实践篇之Luna

创建世界的主角 父子物体 首先创建一个cube物体 可以观察到其在2D视角下的坐标为&#xff08;0&#xff0c;0&#xff09; 此时将cube物体拖拽到ldle_0下&#xff0c;如图所示&#xff0c;并将其坐标值改为&#xff08;2&#xff0c;2&#xff09; 此时再将ldle_0物体的坐标…

拓数派入选中电联大数据与统计分会两大重点专项工作组

自中国电力企业联合会大数据与统计分会成立以来&#xff0c;深入贯彻党中央、国务院关于不断做强做优做大我国数字经济有关要求&#xff0c;充分发挥数据要素乘数效应&#xff0c;凝聚行业专家及能源电力产业链各主体力量&#xff0c;持续推进能源电力数据资源交易共享&#xf…

Unity环绕物体的摄像机,添加了遮挡的适应

第三人人称摄像机 支持的功能 设定目标后使用鼠标可以环绕目标点旋转&#xff0c;且会进行遮挡的适配&#xff0c;当有遮挡的时候会移动差值移动到没有遮挡的位置。 使用方式 将vThirdPersonCamera 挂在与摄像机上然后为target赋值。 如果有需要检测遮挡的层级可以修改&…

数据仓库与数据挖掘实验练习6-7(实验四2024.5.22)

tips&#xff1a; 列出虚拟环境&#xff1a;conda env list 激活虚拟环境&#xff1a;activate hi 进入jupyter-lab&#xff1a;jupyter lab 练习6 1. 处理字符串空格 发现问题: 使用 values 属性查看数据时&#xff0c;如果发现 Name 列没有对齐&#xff0c;很可能是 Name 左…

2024年软考总结 信息系统管理师

选择题 英文题&#xff0c;我是一题也没把握&#xff0c;虽然我理解意思。 千万不要认为考死记硬背不对。目的不在于这。工程项目中有很多重要的数字&#xff0c;能记住说明你合格。 案例 几乎把答案全写在案例中了。 计算题 今年最简单。没有考成本。 只考了关键路径&a…

sheng的学习笔记-AI-EM算法

AI学习笔记目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 目录 基础知识 什么是EM算法 EM算法简介 数学知识 极大似然估计 问题描述 用数学知识解决现实问题 最大似然函数估计值的求解步骤 Jensen不等式 定义 EM算法详解 问题描述 EM算法推导流程 EM算法流程…

绘制t-SNE图

什么是t-SNE图&#xff1f; 如下图&#xff0c;下图来源于论文Contrastive Clustering 一般用于分类问题/对比学习。 作用&#xff1f; 体现出经过层层训练&#xff0c;类内越来越紧密&#xff0c;类间差异越来越大&#xff1b;或者也可以做消融可视化。 怎么画&#xff1f…

如何安装虚拟机Wmware,并且在虚拟机中使用centos系统

1. 前言 大家好&#xff0c;我是jiaoxingk 本篇文章主要讲解如何安装虚拟机&#xff0c;并且在虚拟机中安装centos系统&#xff0c;让windows电脑也能够使用Linux系统 2. 虚拟机的介绍 在安装Vmware之前&#xff0c;我们先做虚拟机的介绍 虚拟机&#xff1a;通过软件虚拟出来的…

【吊打面试官系列】Java高并发篇 - 什么是乐观锁和悲观锁?

大家好&#xff0c;我是锋哥。今天分享关于 【什么是乐观锁和悲观锁?】面试题&#xff0c;希望对大家有帮助&#xff1b; 什么是乐观锁和悲观锁? 1、乐观锁&#xff1a; 就像它的名字一样&#xff0c;对于并发间操作产生的线程安全问题持乐观状态&#xff0c; 乐观锁认为竞争…

JAVAEE初阶多线程(4)

在前面的文章中简单的概述了线程的基本定义接下来就是线程的最后完结了。 1.工厂模式 1.1工厂模式的简单定义 &#xff08;1&#xff09;在java jar包中有一个工厂模式这是一种设计模式 &#xff08;2&#xff09;这个设计模式是为了更好的解决构造方法创建对象太坑了的问题…

安卓开发:相机水印设置

1.更新水印 DecimalFormat DF new DecimalFormat("#"); DecimalFormat DF1 new DecimalFormat("#.#");LocationManager LM (LocationManager)getSystemService(Context.LOCATION_SERVICE); LM.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2…

urllib_post请求_百度翻译之详细翻译

百度翻译有一个详细翻译的接口&#xff1a; post请求&#xff1a; 请求参数&#xff08;较多&#xff09;&#xff1a; 打印之后&#xff0c;发现有问题&#xff1a; 改一下请求头&#xff1a; 将Accept-Encoding注释掉&#xff0c;因为我们使用的是utf-8编码&#xff1a; 加上…

解决:LVGL+GUI Guider 1.7.2运行一段时间就会卡死死机,内存泄露溢出的问题

概括&#xff1a; 我在使用NXP官方GUI Guider生成的代码出现了内存泄漏的问题。但我遇到的并不是像其他人所说的style的问题&#xff0c;如下链接。而是因为在页面渲染之前就使用了该页面内的组件&#xff0c;内存就会不断增加。 LVGL 死机 内存泄漏_lvgl 内存溢出-CSDN博客 运…