哈希表——拉链法、开放寻址法、字符串前缀和哈希

文章目录

    • 拉链法
    • 开放寻址法
    • unordered_map
    • 字符串哈希

哈希表又称散列表
哈希表——拉链法、开放寻址法、字符串前缀和哈希_第1张图片

哈希表的内容,我们需要掌握两个内容:
1.哈希表的存储结构;包括①拉链法②开放寻址法
2.字符串的哈希方式
拉链法:
哈希表——拉链法、开放寻址法、字符串前缀和哈希_第2张图片
开放寻址法:
哈希表——拉链法、开放寻址法、字符串前缀和哈希_第3张图片

例题:模拟散列表

哈希表——拉链法、开放寻址法、字符串前缀和哈希_第4张图片

拉链法

哈希表——拉链法、开放寻址法、字符串前缀和哈希_第5张图片
哈希表——拉链法、开放寻址法、字符串前缀和哈希_第6张图片

哈希表——拉链法、开放寻址法、字符串前缀和哈希_第7张图片
哈希表——拉链法、开放寻址法、字符串前缀和哈希_第8张图片

#include 
#include 

using namespace std;

const int N = 1e5 + 3;//取大于1e5的第一个质数,这样取质数冲突的概率最小

int h[N], e[N], ne[N], idx;//数组h是开一个槽(拉链),相当于在每个点下开一个链表

void insert(int x)
{
    //c++中如果是负数,那他取模也是负的,所以 加N 再 % N 就定是一个正数
    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;
    cin >> n;
    memset(h, -1, sizeof h);
    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;
}

开放寻址法

哈希表——拉链法、开放寻址法、字符串前缀和哈希_第9张图片

#include 
#include 

using namespace std;

//开放寻址法一般开 数据范围的 2~3倍, 这样大概率就没有冲突了
const int N = 200003, null = 0x3f3f3f3f;//大于数据范围的第一个质数,null是大于1e9的数

int h[N];

int find(int x)
{
    int t = (x % N + N) % N;
    while(h[t] != null && h[t] != x)
    {
        t++;
        if(t == N) t = 0;
    }
    return t;//如果这个位置是空的, 则返回的是他应该存储的位置
}

int main()
{
    memset(h, 0x3f, sizeof(h));
    int n;
    scanf("%d", &n);
    
    while(n--)
    {
        char op[2];
        int x;
        scanf("%s%d", op, &x);
        if(*op == 'I') h[find(x)] = x;
        else
        {
            if(h[find(x)] == null) puts("No");
            else puts("Yes");
        }
    }
    
    return 0;
}

unordered_map

其实C++中的STL中已经帮我们封装好了哈希表,我们直接拿来用就可以了;
就是 unordered_map
unordered_map是将一个key值和value值相对应起来的容器,就类似于Python中的字典用法
key值是唯一的;key和value的数据类型可以不同
unordered_map存储元素是没有顺序的
可以使用 [] 操作符来访问key值对应的value值

字符串哈希

字符串哈希全称是字符串前缀哈希,把字符串上的各个字母变成P进制(哈希值),从而可以实现不同的字符串映射到不同的数字。形如 X1X2X3⋯Xn−1XnX1X2X3⋯Xn−1Xn 的字符串,采用字符的Ascii 码乘上 P 次方来计算哈希值。映射公式 (X1×Pn−1+X2×Pn−2+⋯+Xn−1×P1+Xn×P0) mod Q
注意点:
1.任何字母不可以映射为0;如字符串A映射为0,而字符串AA映射也为0
2.冲突问题:一般我们P设置为131或13331,Q设置为2^64(在代码中我们不需要设置Q,unsigned long long正好可以解决这样的问题)
字符串哈希使用场景:判断两个字符串是否相等
前缀和公式 h[i+1]=h[i]×P+s[i]h[i+1]=h[i]×P+s[i] i∈[0,n−1]i∈[0,n−1] h为前缀和数组,s为字符串数组
区间和公式 h[l,r]=h[r]−h[l−1]×Pr−l+1
区间和公式的理解: ABCDE 与 ABC 的前三个字符值是一样,只差两位,
乘上 P2P2 把 ABC 变为 ABC00,再用 ABCDE - ABC00 得到 DE 的哈希值。

例题:
哈希表——拉链法、开放寻址法、字符串前缀和哈希_第10张图片

#include 
#include 

using namespace std;

typedef unsigned long long ULL;

const int N = 1e5 + 10, P = 131;

int n, m;
char str[N];
ULL h[N], p[N];//数组p为p的多少次方

ULL get(int l, int r)
{
    return h[r] - h[l - 1] * p[r - l + 1];
}

int main()
{
    scanf("%d%d", &n, &m);
    scanf("%s", str + 1);
    p[0] = 1;//注意这步千万不要忘了 最开始的权值必须赋值为1 否则接下来就会出错
    for(int i = 1; i <= n; ++i)
    {
        h[i] = h[i - 1] * P + str[i];//计算每个位上的相应权值
        p[i] = p[i - 1] * P;//计算字符串前缀值,最新加入的数的权值为p的0次 所以直接加上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;
}

你可能感兴趣的:(算法基础课,哈希算法,散列表,数据结构)