c++哈希表——超实用的数据结构

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...m1] 的下标上 ( 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,...,P1),每一类用一个单独的链表存储。
  • 查找整数 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⋅⋅⋅sn1
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)=s0pn1+s1pn2+⋅⋅⋅+sn1p0

代码:

#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.结语

今天的文章就到这里啦,三连必回哦!

  • 21
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值