一日一题:第十二题---模拟散列表(三种方法!!)

​作者:小妮无语
专栏:一日一题

🚶‍♀️✌️道阻且长,不要放弃✌️🏃‍♀️

今天来给大家介绍的是简单的Hash表的应用


目录

关于哈希的知识点 

题目描述(模拟散列表)

代码 1(拉链法)

 代码 2(开放寻址法)

代码 3 (map结构)

小记 


关于哈希的知识点 


 直达链接

题目描述(模拟散列表)

维护一个集合,支持如下几种操作:

  1. I x,插入一个数 x;
  2. Q x,询问数 x 是否在集合中出现过;

现在要进行 N 次操作,对于每个询问操作输出对应的结果。

输入格式

第一行包含整数 N,表示操作数量。

接下来 N 行,每行包含一个操作指令,操作指令为 I xQ 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

代码 1(拉链法)

//拉链法
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+3;//最好是质数,且离2的次方远点
int n[N],ne[N],idx,k[N];//这里采用的技术就是单链表
void insert(int x)
{
    int m=(x%N+N)%N;//构造哈希函数,为了防止出现负数所以加了N又mod了N,如果是正数就没影响
    n[idx]=x;//就是现在索引被存到了idx
    ne[idx]=k[m];//ne[idx],idx这个数的下一个数的地址
    k[m]=idx++;//就是相同m的这槽(像是表头)这个表的头指针是什么,通过表头往后查找数据
}
bool find(int x)
{
    int m=(x%N+N)%N;
    for(int i=k[m];i!=-1;i=ne[i])//通过每个位置在上挂的数组的表头往后找,一般一个位置不会挂太多数,时间复杂度与等于1
    {
        if(n[i]==x)
        {
            return true;
        }
    }
    return false;
}
int main()
{
    int h;
    cin>>h;
    memset(k,-1,sizeof k);//把数组先全搞成数据范围外的数
    while(h--)
    {
        char a[2];
        int x;
        cin>>a>>x;
        
        if(a[0]=='I')
        {
            insert(x);
        }
        else
        {
            if(find(x))puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

 代码 2(开放寻址法)

//开放寻址法
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int N=2*1e5+3;//数组开2~3倍大,最好是质数,且离2的次方远点
const int null=0x3f3f3f3f;//在int范围里都可以算得上无穷大了
int n,g[N];
// map<int,int> g;
int find(int x)//作用:找个能存x的地方
{
    int m=(x%N+N)%N;//构造哈希函数,为了防止出现负数所以加了N又mod了N,如果是正数就没影响
    while(g[m]!=x&&g[m]!=null)
    {
        m++;
        if(m>=N) m%=N;
        //如果数组到头了,其实并不一定是存满了,
        //有可能是你要存的数太大了,就从头找个空位或者肯是否出现过
    }
    return m;//返回要么是空的要么等于x的下标,几乎不可能不返回,以为我们N开始就开的很大
}
int main()
{
    cin>>n;
    memset(g,null,sizeof g);//把数组先全搞成数据范围外的数
    while(n--)
    {
        char a[2];
        int x;
        cin>>a>>x;
        int u=find(x);
        //找到可以存放x的地方,这个题,其实有点像是默认了只要出现过就OK
        //而且不用担心是否有重复的数
        if(a[0]=='I')
        {
            g[u]=x;
        }
        else
        {
            if(g[u]==x)puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

代码 3 (map结构)

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int n;
map<int,int> g;
int main()
{
    cin>>n;
    
    while(n--)
    {
        char a[2];
        int x;
        cin>>a>>x;
        if(a[0]=='I')
        {
            g[x]=1;
        }
        else
        {
            if(g[x])puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

小记 

上述三个方法我最喜欢第二个,哈哈哈,第三个虽然好用,但从时间上看是前面两种的两倍,开放寻址法和拉链法默认的时间复杂度都是O(1) 

map函数我不经常用,有次看到一个大佬这样写的,感觉很好用,应该后续会学到

开放寻址法思想很简单,就是你通过哈希函数来算出一个值,看看该值的位置是否为空,不为空就往后找,这里默认不会找太远,所以时间复杂度固定的O(1)

拉链法需要写两个函数,插入函数,采用的是单链表的插入,查找函数,也是模仿链表的查找,通过哈希函数来算出一个值,然后把值相同的数串在一起,根据idx指针索引来进行查找,因为默认的一个位不会串太多元素,所以时间复杂度也给的O(1)

          == 欢迎来到一日一题的小菜鸟频道,睡不着就看看吧!==

跟着小张刷题吧!

                     ==  跟着小张刷题吧!==

 

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值