哈希学习笔记
哈希(HASH)
哈希(HASH)本质上是一种映射。
引入1
给定
方法
用一个桶来统计每一个数的次数,最后循环值域,如果次数不为
时间复杂度:
引入2
给定
这题发现值域到了
方法1
用STL-unique来写,时间复杂度大概为
不过大部分题目都无法用unique,就有了新的算法-哈希。
方法2-哈希算法
我们用一个函数
一般都将哈希函数
最后将哈希之后的数做桶排即可。
哈希冲突
我们发现如果两个数
解决哈希冲突
我们用一个链表处理
每次插入时遍历
时间复杂度
此时的时间复杂度就十分优秀了。
模板代码
#include <bits/stdc++.h> using namespace std; const int MOD = 999983; int n, x, ans; vector <int> G[MOD + 5]; int Hash (int x) { return x % MOD; } int insert (int x) { int val = Hash(x); for (int i = 0; i < G[val].size(); ++i) { if (G[val][i] == x) return 0; } G[val].push_back(x); return 1; } int main() { cin >> n; for (int i = 1; i <= n; ++i) { cin >> x; ans += insert(x); } cout << ans << endl; return 0; }
例题
这题定义哈希函数
代码:
#include <bits/stdc++.h> using namespace std; const int N = 5e4 + 5; const int MOD = 999983; int T, n, ans; int a[N]; vector <int> G[MOD + 5]; int Hash (int x) { return (x % MOD + MOD) % MOD; } int insert (int x) { int val = Hash(x); for (int i = 0; i < G[val].size(); ++i) { if (G[val][i] == x) return 0; } G[val].push_back(x); return 1; } int main() { cin >> T; while (T--) { cin >> n; ans = 0; for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); } for (int i = 0; i < MOD; ++i) { G[i].clear(); } for (int i = 1; i <= n; ++i) { if (insert(a[i]) == 1) { cout << a[i] << " "; } } cout << endl; } return 0; }
首先先看题目,看到有
可发现数据范围中的
就知道用纯的并查集过不去。于是想到了用哈希将数的范围缩小,随后并查集维护即可。哈希函数一样是
代码:
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; const int MOD = 990887; int T, n; int x[N], y[N], opt[N]; int fa[MOD + 5]; int Hash (int x) { return x % MOD; } int find (int x) { if (x == fa[x]) return x; else return fa[x] = find(fa[x]); } void unionn (int x, int y) { int fx = find(x), fy = find(y); if (fx != fy) fa[fx] = fy; return ; } int main() { cin >> T; while (T--) { cin >> n; for (int i = 0; i < MOD; ++i) { fa[i] = i; } for (int i = 1; i <= n; ++i) { cin >> x[i] >> y[i] >> opt[i]; if (opt[i] == 1) unionn(Hash(x[i]), Hash(y[i])); } bool flag = false; for (int i = 1; i <= n; ++i) { if (opt[i] == 0) { if (find(Hash(x[i])) == find(Hash(y[i]))){ puts("NO"); flag = true; break; } } } if (flag == false) puts("YES"); } return 0; }
样例输入(POJ):
2 1 2 3 4 5 6 4 3 2 1 6 5
样例输出(POJ):
Twin snowflakes found.
首先看到这题,发现每个雪花是一个含有六个数的序列,我们就定义这个序列的哈希值为这些数的和
(当然也可以是别的,比如这个序列的和加这个序列的乘积
需要注意的是,判断两个雪花是否相同时,从某个节点,顺时针和逆时针都要比较才行。
代码:
#include <iostream> #include <cstdio> #include <vector> #define int long long using namespace std; const int N = 1e5 + 5; const int MOD = 99991; int n; int a[N][20]; vector <int> G[MOD + 5]; int Hash (int ind) { int sum = 0; for (int i = 1; i <= 6; ++i) { sum = (sum + a[ind][i]) % MOD; } return sum % MOD; } bool issame (int ind1, int ind2) { for (int i = 1; i <= 6; ++i) { for (int j = 1; j <= 6; ++j) { bool flag = true; for (int k = 0; k < 6; ++k) { if (a[ind1][(i + k) % 6 + 1] != a[ind2][(j + k) % 6 + 1]) flag = false; } if (flag == true) return true; flag = true; for (int k = 0; k < 6; ++k) { if (a[ind1][(i + k) % 6 + 1] != a[ind2][(j - k + 6) % 6 + 1]) flag = false; } if (flag == true) return true; } } return false; } int insert (int ind) { int val = Hash(ind); for (int i = 0; i < G[val].size(); ++i) { if (issame(G[val][i], ind)) return 1; } G[val].push_back(ind); return 0; } int ans = 0; signed main() { cin >> n; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= 6; ++j) { scanf("%lld", &a[i][j]); } if (insert(i) == 1) { puts("Twin snowflakes found."); return 0; } } puts("No two snowflakes are alike."); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步