2.3 数据结构 | Hash表(哈希表)、C++ STL
这是我的一个算法网课学习记录,道阻且长,好好努力
2.3.1 Hash 表
将一个 109 ~ -109 的数映射到0 ~ 105
h(x),哈希函数:1. x mod 105 ,取模这个数最好取成质数且离2的整数幂尽可能的远; 2. 冲突,可能会将很多个数映射到同一个数
哈希算法是一种期望算法(?)
2.3.1.1 存储结构
在数学中负数取模后为正数,但在C++中负数取模后将会为负数。
例如:
数学:-10 % 3 = 2,
C++:-10 % 3 = -1。
解决办法是都加上一个很大的数再取模,也就是转化为绝对值
例题:AcWing 840. 模拟散列表
维护一个集合,支持如下几种操作:
I x
,插入一个数 x;
Q x
,询问数 x是否在集合中出现过;
现在要进行 N次操作,对于每个询问操作输出对应的结果。
- 拉链法
单数组加上数组模拟链表
#include <cstring>
#include <iostream>
using namespace std;
const int N = 100003; // 大于1e5的最小质数
int h[N], e[N], ne[N], idx;
void insert(int x) // 和链表操作类似
{
int k = (x % N + N) % N; // 将正数负数都转化为正数
e[idx] = x;
ne[idx] = h[k];
h[k] = idx ++ ;
}
bool find(int x)
{
int k = (x % N + N) % N;
for (int i = h[k]; i != -1; i = ne[i])
if (e[i] == x)
return true;
return false;
}
int main()
{
int n;
scanf("%d", &n);
memset(h, -1, sizeof h); // 初始化为-1
while (n -- )
{
char op[2];
int x;
scanf("%s%d", op, &x);
if (*op == 'I') insert(x);
else
{
if (find(x)) puts("Yes");
else puts("No");
}
}
return 0;
}
- 开放寻址法
#include <iostream>
#include <cstring>
using namespace std;
const int N = 200003, null = 0x3f3f3f3f; // 一般开数组范围是给定数据个数的2-3倍; null是在N之外的一个数
int h[N];
int find(int x)
{
int k = (x % N + N) % N;
while (h[k] != null && h[k] != x)
{
k ++ ;
if (k == N) k = 0; // 返回最开始
}
return k;
}
int main()
{
int n;
scanf("%d", &n);
memset(h, null, sizeof h); // 初始化
while (n -- )
{
char op[2];
int x;
scanf("%s%d", op, &x);
int k = find(x);
if (*op == 'I') h[k] = x;
else
{
if (h[k] != null) puts("Yes");
else puts("No");
}
}
return 0;
}
2.3.1.2 字符串哈希方式
字符串前缀哈希方式:直接存储一个很长的字符串需要大量空间,哈希对此进行优化
哈希表用于快速地存储和查询数据,而数据类型是多样的。
当数据类型为字符时,就可以使用字符串哈希。
核心思想:借助p进制数转换的方法,将字符串视为一个数字,取模存储
注意: 不能将字符映射成0;且假设人品足够好,不会有冲突(p = 131 或 13331;q = 264。取这样的经验值在绝大多数情况下可以解决问题)
好处&作用
:快速判断两个字符串是否相等
例题:AcWing 841.字符串哈希
给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2,请你判断 [l1, r1] 和 [l2, r2] 这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
#include <iostream>
typedef unsigned long long ULL;
using namespace std;
const int N = 100010, P = 131; // or 13331
int n, m;
char str[N];
ULL h[N], p[N];
ULL get(int l, int r)
{
return h[r]- h[l - 1] * p[r - l + 1]; // p[r - l + 1]为要乘以的权数
}
int main()
{
scanf("%d%d%s", &n, &m, str + 1);
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
p[i] = p[i - 1] * P;
h[i] = h[i - 1] * P + str[i]; // 计算字符串前缀的每个哈希值
}
while (m -- )
{
int l1, r1, l2, r2;
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
if (get(l1, r1) == get(l2, r2)) puts("Yes");
else puts("No");
}
return 0;
}
2.3.2 C++ STL
不用背过,随用随查!!
一个看起来很标准的 定义 :
STL(Standard Template Library),即标准模板库,是一个具有工业强度的,高效的C++程序库。它被容纳于C++标准程序库(C++ Standard Library)中,是ANSI/ISO C++标准中最新的也是极具革命性的一部分。该库包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法。为广大C++程序员们提供了一个可扩展的应用框架,高度体现了软件的可复用性。
STL中六大组件:
容器(Container)
迭代器(Iterator)
算法(Algorithm)
仿函数(Functor)
适配器(Adaptor)
分配器(allocator)
在这篇博客中十分精彩和详细的介绍:C++中STL用法超详细总结