模拟散列表——哈希

题目

题目来源Acwing
维护一个集合,支持如下几种操作:

I x,插入一个整数 x
Q x,询问整数 x是否在集合中出现过;
现在要进行 N次操作,对于每个询问操作输出对应的结果。

输入格式
第一行包含整数 N,表示操作数量。

接下来 N行,每行包含一个操作指令,操作指令为 I x,Q x 中的一种。

输出格式
对于每个询问指令 Q x,输出一个询问结果,如果 x在集合中出现过,则输出 Yes,否则输出 No。

每个结果占一行。

数据范围
1≤N≤10的5次方
−10的9次方≤x≤10的9次方
输入样例:
5
I 1
I 2
I 3
Q 2
Q 5
输出样例:
Yes
No

题解

思路:

核心思想:把大范围的数压缩到较小范围数组(存放次数)中
eg:
对一组数(3,39,105,310)采用 x mod 100存放,所有存放位置分别为(3,39,5,10)
1、拉链法
对与多个存放在同一个位置的数,采用一个单链表存储
2、开放寻址法
对与多个存放在同一个位置的数,延后n个位置存储,n为原位置与原位置后的空位置的距离

拉链法

错误案例

本段代码不可运行
拉链法:第一次想为每一个位置都创建一个顺序存储的单链表————内存爆炸
#include<iostream>
using namespace std;
const int N=100003;

struct node{
    int head=-1;
    int idx=0;
    int e[2];
    int ne[2];
};

void add(node q[],int x){
    int k=(x%N+N)%N;
    q[k].e[q[k].idx]=x;
    q[k].ne[q[k].idx]=q[k].head;
    q[k].head=q[k].idx;
    q[k].idx++;
}

void find(node q[],int x){
    int k=(x+N+N)%N;
    if(q[k].head==-1){
        cout<<"No"<<endl;
        return;
    }
    for(int i=q[k].head;i!=-1;i=q[k].ne[i]){
        if(q[k].e[i]==x){
            cout<<"Yes"<<endl; 
            return;
        }
    }
    cout<<"No"<<endl;
}

int main(){
    int n;
    cin>>n;
    while(n--){
        char c;
        int x;
        cin>>c>>x;
        node q[N];
        if(c=='I')add(q,x);
        else find(q,x);
    }
}

正确做法

用一个数组存储多个单链表

#include<iostream>
#include <cstring>
using namespace std;
const int N=100003;
int h[N],e[N],ne[N],idx;

void add(int x){
    int k=(x%N+N)%N;
    e[idx]=x;
    ne[idx]=h[k];
    h[k]=idx;
    idx++;
}

void find(int x){
    int k=(x%N+N)%N;
    for(int i=h[k];i!=-1;i=ne[i]){
        if(e[i]==x){
            cout<<"Yes"<<endl;
            return;
        }
    }
    cout<<"No"<<endl;
    return;
}

int main(){
    int n;
    cin>>n;
    memset(h,-1,sizeof h);
    while(n--){
        char op;
        int x;
        cin>>op>>x;
        if(op=='I')add(x);
        else find(x);
    }
}

开放寻址法一

#include<iostream>
#include<cstring>
using namespace std;
const int N=200003,null = 0x3f3f3f3f;

int e[N];

void add(int x){
    int k=(x%N+N)%N;
    for(int i=k;;i++){
        if(e[i]==null){
            e[i]=x;
            return;
        }
    }
}

void find(int x){
    int k=(x%N+N)%N;
    for(int i=k;;i++){
        if(e[i]==x){
            cout<<"Yes"<<endl;
            return;
        }
        else if(e[i]==null){
            cout<<"No"<<endl;
            return;
        }
    }
}

int main(){
    int n;
    cin>>n;
    memset(e,0x3f,sizeof e);
    while(n--){
        char op;
        int x;
        cin>>op>>x;
        if(op=='I')add(x);
        else find(x);
    }
}

开放寻址法二(y总)

#include<iostream>
#include<cstring>
using namespace std;
const int N=200003,null = 0x3f3f3f3f;

int e[N];

//找每个数的存放位置,没找到返回0
int find(int x){
    int k=(x%N+N)%N;
    for(;;k++){
        if(e[k]==null||e[k]==x){
            break;
        }
        if(k==N) {
            k=0;
            break;
        }
    }
    return k;
}

int main(){
    int n;
    cin>>n;
    memset(e,0x3f,sizeof e);
    while(n--){
        char op;
        int x;
        cin>>op>>x;
        if(op=='I'){
            e[find(x)]=x;
        }
        else {
            //find[0]=0存在两种情况,不能直接find(x)==0
            if(e[find(x)]==null)cout<<"No"<<endl;
            else cout<<"Yes"<<endl;
        }
    }
}
``


  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值