问题
- 怎样取哈希数组长度的大小造成哈希冲突的概率最小?
- 关于用scanf输入单个字符要注意什么?
- string的读入方式及注意事项
思维导图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2TJJ8jAy-1680360268816)(assets/image-20230401131205-14miee8.png)]
- 关键字:类似函数的自变量,根据关键字找到对应的哈希值
- 哈希函数:关键字对应哈希值的方式
- 哈希值:根据映射函数找到的数据
- 哈希冲突:不同的关键字对应了相同的哈希值
- 扩容:当哈希表的数据存储达到哈希表容量的百分之七十即会开始扩容。
拉链法
拉链法并不是字面意义上的拉链,不是名词,而是动词。即在对应的哈希值下拉一条链表。
模板题:
https://www.acwing.com/activity/content/problem/content/890/
做着还是比较顺利的,不多做详解了。
代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 100003;
int e[N], h[N], ne[N], idx;
bool find(int val) {
int k = (val % N + N) % N;
for(int i = h[k]; i != -1; i = ne[i])
if(e[i] == val) return true;
return false;
}
void insert(int val) {
int k = (val % N + N) % N;
e[idx] = val;
ne[idx] = h[k];
h[k] = idx ++;
}
int main() {
int n, val;
scanf("%d", &n);
memset(h, -1, sizeof h);
while(n --) {
char c[2];
scanf("%s%d", c, &val);
if(*c == 'I') insert(val);
else {
if(find(val)) puts("Yes");
else puts("No");
}
}
return 0;
}
开放寻址法
开放寻址法的模板(使用线性探测法):
#include iostream
#include cstring
using namespace std;
const int N = 200003; // 哈希表最大容量
int h[N], e[N], ne[N], idx; // h[k] 表示哈希值为 k 的键值对在链表中的头节点下标,e[] 存储节点的键值,ne[] 存储节点的下一个节点下标,idx 存储当前已经分配了多少个节点
int p[N]; // 存储每个键值对所在的下标
void insert(int x)
{
int k = (x % N + N) % N; // 这里需要注意,C++取模操作返回的是余数,可能是负数,需要将其转化为正数
e[idx] = x;
if (!h[k]) h[k] = idx;
else
{
for (int i = h[k]; i != -1; i = ne[i])
if (e[i] == x)
return;
ne[idx] = h[k];
h[k] = idx;
}
p[idx ++] = k;
}
bool find(int x)
{
int k = (x % N + N) % N;
if (!h[k]) return false;
for (int i = h[k]; i != -1; i = ne[i])
if (e[i] == x)
return true;
return false;
}
void remove(int x)
{
int k = (x % N + N) % N;
if (!h[k]) return;
if (e[h[k]] == x)
{
h[k] = ne[h[k]];
return;
}
for (int i = h[k]; ne[i] != -1; i = ne[i])
if (e[ne[i]] == x)
{
ne[i] = ne[ne[i]];
return;
}
}
int main()
{
memset(h, -1, sizeof h);
int n;
cin >> n;
while (n -- )
{
char op;
int x;
cin >> op >> x;
if (op == 'I') insert(x);
else if (op == 'Q')
{
if (find(x)) puts("Yes");
else puts("No");
}
else remove(x);
}
return 0;
}
该模板中实现了插入、查找和删除三个基本操作。其中,插入时使用线性探测法解决哈希冲突,如果哈希值相同则往后依次搜索直至找到空闲位置;查找时先根据哈希值定位到对应的链表头,然后遍历链表搜索指定键值;删除时先定位到对应的链表头,然后遍历链表删除指定键值所在的节点。
答案
- 是质数,并且尽量离二的次方远。所以在规定模长的时候,可以写一个程序看看大于数据范围的第一个质数是什么。
- 尽量使用scanf输入字符串,因为scanf会将空格和换行自动忽略,避免后续调整
- 设字符串为str,有以下几种读入方式:
cin >>str, getline(cin, str)
,注意事项:不可用scanf读入字符串,否则会段错误。