哈希表1.0

本文介绍了哈希表处理冲突的两种方法——拉链法和开放寻址法,分别提供了相应的C++实现。拉链法在哈希值相同的位置建立链表,开放寻址法则通过线性探测解决冲突。此外,文章还提到了哈希函数选择的重要性,建议使用质数作为模长,并讨论了输入输出操作,如使用scanf进行单个字符输入和字符串读入的注意事项。
摘要由CSDN通过智能技术生成

问题

  1. 怎样取哈希数组长度的大小造成哈希冲突的概率最小?
  2. 关于用scanf输入单个字符要注意什么?
  3. 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;
}

该模板中实现了插入、查找和删除三个基本操作。其中,插入时使用线性探测法解决哈希冲突,如果哈希值相同则往后依次搜索直至找到空闲位置;查找时先根据哈希值定位到对应的链表头,然后遍历链表搜索指定键值;删除时先定位到对应的链表头,然后遍历链表删除指定键值所在的节点。

答案

  1. 是质数,并且尽量离二的次方远。所以在规定模长的时候,可以写一个程序看看大于数据范围的第一个质数是什么。
  2. 尽量使用scanf输入字符串,因为scanf会将空格和换行自动忽略,避免后续调整
  3. 设字符串为str,有以下几种读入方式:cin >>str, getline(cin, str),注意事项:不可用scanf读入字符串,否则会段错误。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值