哈希表
哈希表
引入:
如果我们要存放一堆数据,但是这些数据很大,直接用 \(vis\) 之类的不能忍一下。
而且要求尽量做到 \(O(1)\) 查询
那么就引入了哈希表。
定义:
散列表(又称哈希表,\(Hash Table\))是一种常用数据结构。它按照哈希特征分类存放,能够实现插入 \(O(1)\),查询均摊较低,几乎可做到 \(O(1)\) ,最差 \(O(n)\)。
实现过程:
定义数组:
int val[N],edge[N],nxt[N],head[N],tot;
//val表示该点对应的值,edge表示该点对应的值的权值,查询时给出。
插入:
插入对应的值和权值,在链表上查询有无点对应的权值是给定的值,如果没有就新建节点。
过程就是链表的建立过程。
void insert(int k,int c){
int x=k%mod;
for(int i=head[x];i;i=nxt[i]){
if(val[i]==k){
edge[i]=c;//这里情况比较多变
return;
}
}
val[++tot]=k; edge[tot]=c;
nxt[tot]=head[x]; head[x]=tot;
}
查询:
通过链表搜索数据,返回值即可。
int calc(int k){
int x=k%mod;
for(int i=head[x];i;i=nxt[i]){
if(val[i]==k) return edge[i];
}
return 0;
}
例题:
题意:
给定两个集合(数组) \(a,b\),求有多少数对 \((i,j)\) 满足 \(a_i \otimes b_i\) 在二进制下包含两个 \(1\)
分析:
暴力就是 \(O(n^2)\) ,随便写。
二进制下包含两个 \(1\) ,表示这两个数二进制下只有两位不同。
即 \(a_i \otimes (2^x)=b_j \otimes (2^y)\) ,即恰好两位不一样,那么就有了一个满足的数对。
我们用 哈希表 记录每一个 \(a_i \otimes 2^{0,1,2...29}\) 。
为了防止重复,先判断哈希表中有没有存在 \(b_{[1,m]} \otimes 2^j\) 的值,然后再插入 \(a_{[1,n]} \otimes 2^j\) 即可。
插入时,记得权值要为加和,而不是直接等于。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+40;
const int mod=1e7+19;
int n,m;
int a[N],b[N],ans;
int val[N],edge[N],nxt[N],head[N],tot;
void insert(int k,int c){
int x=k%mod;
for(int i=head[x];i;i=nxt[i]){
if(val[i]==k){
edge[i]+=c;//这里权值是+=
return;
}
}
val[++tot]=k; edge[tot]=c;
nxt[tot]=head[x]; head[x]=tot;
}
int calc(int k){
int x=k%mod;
for(int i=head[x];i;i=nxt[i]){
if(val[i]==k) return edge[i];
}
return 0;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=m;i++) scanf("%d",&b[i]);
for(int i=0;i<=29;i++){
int k=1<<i;
for(int j=1;j<=m;j++) ans+=calc(b[j]^k);
for(int j=1;j<=n;j++) insert(a[j]^k,1);
}
printf("%d\n",ans);
system("pause");
return 0;
}
注意事项:
哈希表一般对于查询时值太大很好用,但是有风险性。
开数组时,尽量不要开太大,避免出锅直接炸内存。
常用的模数:\(19260817\)(危险) , \(1e7+19\)。
不关注的有难了😠😠😠https://b23.tv/hoXKV9