文章目录
- 1. 概念引入
- 1.1 整数哈希
- 1.1.1 直接取余法。
- 1.1.2 哈希冲突
- 1.1.2.1 开放寻址法
- 1.1.2.2 拉链法
- 1.2 字符串哈希
- 3.结语
1. 概念引入
- 哈希表是一种高效的数据结构 。
- H a s h Hash Hash表又称为散列表,一般由 H a s h Hash Hash函数(散列函数)与链表结构共同实现。
- 散列(映射)方法是使用函数 h h h 将元素 U U U映射到表 T [ 0... m − 1 ] T[0...m-1] T[0...m−1] 的下标上 ( m = O ( ∣ U ∣ ) ) (m=O(|U|)) (m=O(∣U∣))。这样以 U U U中关键字为自变量,以h为函数的运算结果。
就是相应结点的存储地址。从而达到在 O ( 1 ) O(1) O(1)时间内就可完成查找。
1.1 整数哈希
我们以一道例题来举例:哈希表。
这道题目是这么做的:
1.1.1 直接取余法。
关键字 k k k除以 m m m,取余数作为在 H a s h Hash Hash表中的位置。
函数表达式可以写成:
哈希函数 h ( k ) = k h(k) = k h(k)=k m o d mod mod m m m
一般 m m m 选择为素数,建议选择 2 e 5 + 10 2e5+10 2e5+10。
1.1.2 哈希冲突
- H a s h Hash Hash函数把复杂信息映射到一个容易维护的值域内。
- 值域变小,有可能造成两个不同的信息被 H a s h Hash Hash函数映射为相同的值(两数同余), H a s h Hash Hash冲突,需要处理这种情况。
1.1.2.1 开放寻址法
- 使用 H a s h Hash Hash函数 h h h把整数 x x x映射为 h [ x ] h[x] h[x],如果 h [ x ] h[x] h[x]已经有值,说明当前查询到的地址发生了冲突。
- 如果当前地址发生冲突,就向这个地址的右边继续查询,直到遇到 N U L L NULL NULL或值 x x x为止。
代码:
#include<bits/stdc++.h>
using namespace std;
#define PII pair<int, int>
#define For(i, a, b) for(int i = a;i <= b;i++)
const int N = 2e5 + 3;
const int null = 0x3f3f3f3f;
int n, h[N];
int get(int x){int idx = (x % N + N) % N;while (h[idx] != null && h[idx] != x){idx = (idx == N ? 0 : idx + 1);}return idx;
}
signed main(){ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);memset(h, 0x3f, sizeof h);int n, x;string op;cin >> n;while (n--){cin >> op;cin >> x;if (op[0] == 'I'){h[get(x)] = x;}else{cout << (h[get(x)] == x ? "Yes\n" : "No\n");}}return 0;
}
1.1.2.2 拉链法
- Hash函数设为 h ( x ) = x h(x) = x % P h(x)=x ,这里 P P P 是较大质数,但不超过数组大小 N N N。
- 这个 H a s h Hash Hash函数 h h h 把整数分为了 P P P 类( M o d P = 1 , 2 , . . . , P − 1 Mod P = 1, 2, ..., P-1 ModP=1,2,...,P−1),每一类用一个单独的链表存储。
- 查找整数 x x x 的时候,就在整数 x x x 所在类的链表里进行查找。
代码:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define PII pair<int, int>
#define For(i, a, b) for(int i = a;i <= b;i++)
const int N = 2e5 + 3;
int head[N], val[N], nxt[N], idx;
void add(int x){int k = (x % N + N) % N;val[idx] = x;nxt[idx] = head[k];head[k] = idx++;
}
bool get(int x){int k = (x % N + N) % N;int res = head[k];while (res != -1){if (val[res] == x){return 1;}res = nxt[res];}return 0;
}
signed main(){ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);memset(head, -1, sizeof head);int n;cin >> n;while (n--){char op;cin >> op;int x;cin >> x;if (op == 'I'){add(x);}else{if (get(x)){cout << "Yes\n";}else{cout << "No\n";}}}return 0;
}
1.2 字符串哈希
字符串 H a s h Hash Hash(字符串前缀 H a s h Hash Hash法),把字符串 s s s 变成一个 p p p 进制数字( H a s h Hash Hash值),实现不同的字符串映射到不同的数字。
字符串 s s s 中 的每个字符本质上就是一个数字( A S C I I ASCII ASCII值)。
s = s 0 s 1 s 2 s 3 ⋅ ⋅ ⋅ s n − 1 s = s_0 s_1s_2s_3···s_n - 1 s=s0s1s2s3⋅⋅⋅sn−1
h ( s ) = s 0 ⋅ p n − 1 + s 1 ⋅ p n − 2 + ⋅ ⋅ ⋅ + s n − 1 ⋅ p 0 h(s) = s_0·p^{n-1}+s_1·p^{n-2}+···+s_n-1·p^0 h(s)=s0⋅pn−1+s1⋅pn−2+⋅⋅⋅+sn−1⋅p0
代码:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int unsigned long long
#define PII pair<int, int>
#define For(i, a, b) for(int i = a;i <= b;i++)const int N = 1e5 + 10;
int n, m;
char s[N];
int h[N], p[N];int get(int l, int r){return h[r] - h[l - 1] * p[r - l + 1];
}signed main(){ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);cin >> n >> m >> s + 1;h[0] = 0;p[0] = 1;For (i, 1, n){h[i] = h[i - 1] * 131 + s[i];p[i] = p[i - 1] * 131;}while (m--){int l1, r1, l2, r2;cin >> l1 >> r1 >> l2 >> r2;if ((get(l1, r1)) == get(l2, r2)) {cout << "Yes\n";}else{cout << "No\n";}}return 0;
}
3.结语
今天的文章就到这里啦,三连必回哦!