处理哈希冲突-线性探测法模拟实现(c++)

题⽬描述:
维护⼀个数据结构,初始时为空。请⽀持下⾯的操作:
1 x :插⼊元素 x
2 x :查询 x 是否在数据结构中。
现有 n 次操作,针对每次查询,输出是否在该数据结构中。
输⼊描述:
第⼀⾏⼀个整数 n ,表⽰查询次数。
之后 n ⾏,第 i ⾏两个整数 op x ,分别表⽰第 op 个操作,以及元素 x
测试⽤例:
12 
1 1 
1 2 
1 3 
1 4 
1 5 
2 2 
1 6 
2 5 
1 7 
2 8 
2 4 
2 25

上篇已经了解过,根据题的数据范围创建的数组大小是之前的两倍,然后在两倍的基础上找一个质数就可以了,比如在这个测试用例里面最大会插入10,所以要把10扩大两倍,在20的左右到一个质数,我们一般会找比他大的质数,23,所以这道题创建的数组大小是23,如果这道题的数据范围是1e5的话,就会按照这个方式先×2,再2e5一直加1直到加到是质数就停下来,创建之后把里面的全都初始化为这道题里面绝对不会出现题目给的数,可以直接把它初始化为0x3f3f3f3f,这是C++里面定义无穷大的形式

创建

#include <iostream>
#include <cstring>
using namespace std;

const int N = 23, INF = 0x3f3f3f3f; //INF是c++形式的无穷大
int h[N]; // 哈希表

// 初始化
void init()
{
    memset(h, 0x3f, sizeof h);
}

int main()
{
    init();
    return 0;
}

2 哈希函数以及处理哈希冲突

除留余数法: hash(key) = key % N
但是要注意, key 有可能是负数,取模之后会变成负数。
  • 负数补正的操作为:加上模数即可。
  • 但是正数加上模数会变⼤,所以统⼀再取⼀次模。
最终就是 (key % N + N) % N ,简称 模加模,比如 -5%23 = 负数,便让 (-5 % 23 + 23)% 23,正数5也是统一处理,(5 % 23 + 23)% 23,模出来结果都为 3
// 哈希函数 - 计算出 x 的存储位置
int f(int x)
{
    //x可能是负数
    int id = (x % N + N) % N;

    // 处理哈希冲突 - 线性探测法
    // 如果模出来的下标被占用,继续向后走;x可能之前存过
    while (h[id] != INF && h[id] != x)
    {
        id++;
        if (id == N) id = 0; // 如果走到尾部,从头开始
    }

    //返回下标
    return id;
}

添加元素

通过哈希函数找到合适的位置,然后放上去即可
void insert(int x)
{
    int id = f(x);
    h[id] = x;
}

查找元素

通过哈希函数找到映射位置,看看⾥⾯的值是不是 x
bool find(int x)
{
    int idx = f(x);
    return h[idx] == x;
}

有人会问为什么没有删除元素的操作,因为线性探测法,删除元素是很麻烦的,我们不能在这个哈希表中直接把元素删除,比如把12这个元素删除,由于有元素可能是通过线性探测过来的,查找8这个元素的时候,从下标8开始,一直走往后走,走到就下标1会停下来,因为这个12被删除了,这是一个被断开的位置,就探测不到后面8这个元素了,所以在线性探测里面不能真的把它删除了,只能在创建一个bool类型的数组,标记一下这个位置已经被删除掉了才行,这个删除逻辑比较麻烦就不实现了

实现代码:

#include <iostream>
#include <cstring>

using namespace std;

const int N = 23, INF = 0x3f3f3f3f; //INF是c++形式的无穷大

int h[N]; // 哈希表

// 初始化
void init()
{
    memset(h, 0x3f, sizeof h);
}

// 哈希函数 - 计算出 x 的存储位置
int f(int x)
{
    //x可能是负数
    int id = (x % N + N) % N;

    // 处理哈希冲突 - 线性探测法
    // 如果模出来的下标被占用,继续向后走;x可能之前存过
    while (h[id] != INF && h[id] != x)
    {
        id++;
        if (id == N) id = 0; // 如果走到尾部,从头开始
    }

    //返回下标
    return id;
}

//添加元素
void insert(int x)
{
    int id = f(x);
    h[id] = x;
}

// 查找元素
bool find(int x)
{
    int idx = f(x);
    return h[idx] == x;
}

int main()
{
    init();
    int n; cin >> n;

    while (n--)
    {
        int op, x; cin >> op >> x;
        if (op == 1) // 插入
        {
            insert(x);
        }
        else // 查询
        {
            if (find(x)) cout << "yes" << endl;
            else cout << "no" << endl;
        }
    }

    return 0;
}

//输入
//12 1 1 1 2 1 3 1 4 1 5 2 2 1 6 2 5 1 7 2 8 2 4 2 25
//输出
//yes
//yes
//no
//yes
//no

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值