哈希表~~

【概念】

​散列表(Hash table,也叫哈希表)是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数(哈希函数),存放记录的数组称做散列表。 ​

一.存储结构

1.插入

2.搜索

3.删除

例1 Acwing 模拟散列表

【分析】

1.数组模拟,有两种方法解决冲突。

(1) 开放寻址法

只需要开一个数组,找到x映射的位置,如果当前位置有数,就继续往后找,直到找到一个空的位置,插入。

(2) 拉链法(HashMap用的就是这种,也叫线性探测)

需要开一个数组和链表,每当有冲突,就在当前位置的链表上添加一个数。

attention

这两种方法的删除操作都是另开一个bool数组,将要删除的数标记即可。

!!!图里有个地方写错了,h最后位置是 10^5-1 

【代码1 开放寻址法】

#include <cstring>
#include <iostream>
using namespace std;
const int N=100003;
int h[N],null=0x3f3f3f3f;//用一个不在x的范围内的数来判断某位置是否有数
int find(int x){
    int k=(x%N+N)%N;
    while(h[k]!=null&&h[k]!=x){
        k++;//没有找到x,++
        if(k==N) k=0;//走到头了,重新做人
    }
    return k;
}//返回的是x的位置或者x应该在的位置
int main(){
    int n;
    cin>>n;
    memset(h,0x3f,sizeof(h));//于是初始化为0x3f
    while(n--){
        char op[2];
        int x;
        scanf("%s%d",op,&x);
        int k=find(x);
        if(*op=='I') h[k]=x;//该去哪去哪
        else{
            if(h[k]!=null) puts("Yes");//该位置有数
            else puts("No");
        }
    }
    return 0;
}

【代码2 拉链法】

#include <cstring>
#include <iostream>
using namespace std;
const int N=100003;
int h[N],e[N],ne[N],idx;
void insert(int x){
    int k=(x%N+N)%N;
    e[idx]=x;
    ne[idx]=h[k];
    h[k]=idx++;
}
bool find(int x){
    int k=(x%N+N)%N;
    for(int i=h[k];i!=-1;i=ne[i]){
        if(e[i]==x) 
        return true;
    }
    return false;
}//标记x是否在某位置
int main(){
    int n;
    cin>>n;
    memset(h,-1,sizeof(h));
    while(n--){
        char op[2];
        int x;
        scanf("%s%d",op,&x);
        if(*op=='I') insert(x);
        else{
            if(find(x)) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

二.字符串哈希方式

字符串前缀哈希法

【浅浅介绍】

把字符串变成一个p进制数字(哈希值),实现不同的字符串映射到不同的数字。
对形如 X1X2X3⋯Xn−1XnX1X2X3⋯Xn−1Xn 的字符串,采用字符的ascii 码乘上 P 的次方来计算哈希值。

映射公式  (X1×Pn−1+X2×Pn−2+⋯+Xn−1×P1+Xn×P0)modQ 

例 Acwing 字符串哈希

【分析】

1. 因为一个字符串可能非常非常长,因此我们可以把前n个字符映射的数看作一个p进制的数

其中P一般取131或13331,Q一般取2^64(经验值)。

按道理来讲,我们的人品应该足够好,不存在冲突。但是要注意字符不能映射成0,否则有可能出现冲突。

2.

前缀和公式 h[i+1]=h[i]×P+s[i]h[i+1]=h[i]×P+s[i] i∈[0,n−1]i∈[0,n−1] 
区间和公式 h[l,r]=h[r]−h[l−1]×Pr−l+1

 

【代码】

#include <iostream>
using namespace std;
typedef unsigned long long ull;
const int N=100010,P=131;
ull h[N],p[N];//p用来表示p的几次方
int n,m;
char s[N];
ull get(int l,int r){
    return h[r]-h[l-1]*p[r-l+1];
}
int main(){
    scanf("%d%d%s",&n,&m,s+1);
    p[0]=1;
    for(int i=1;i<=n;i++){
        p[i]=p[i-1]*P;
        h[i]=h[i-1]*P+s[i];
    }
    while(m--){
        int l1,r1,l2,r2;
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        if(get(l1,r1)==get(l2,r2)) puts("Yes");
        else puts("No");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值