作者:小妮无语
专栏:一日一题🚶♀️✌️道阻且长,不要放弃✌️🏃♀️
今天来给大家介绍的是简单的Hash表的应用
目录
关于哈希的知识点
题目描述(模拟散列表)
代码 1(拉链法)
代码 2(开放寻址法)
代码 3 (map结构)
小记
关于哈希的知识点
直达链接
题目描述(模拟散列表)
维护一个集合,支持如下几种操作:
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
代码 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)
== 欢迎来到一日一题的小菜鸟频道,睡不着就看看吧!==
== 跟着小张刷题吧!==