数据结构基础8:Hash表

文章介绍了哈希表的基本概念,包括使用哈希函数将大范围的输入映射到较小的范围,并探讨了两种常见的冲突解决方法:拉链法和开放寻址法。拉链法通过链表处理冲突,而开放寻址法则通过寻找下一个空槽来解决冲突。此外,还提到了字符串哈希及其在判断子串是否相同中的应用。
摘要由CSDN通过智能技术生成

Hash表

存储结构

  1. 开放寻址法
  2. 拉链法

将一些比较复杂的数据映射到0-N这些数,比如将0 - 109映射到0 - 105

将输入x (-10^9 ~ + 10^9)通过哈希函数转换为输出y (0 ~ +10^5)

问题:

  1. 哈希函数一般怎么写? x mod 10^5 一般来说模的数要取成质数,冲突的概率会小一点
  2. 有冲突怎么办?即不同的输入x导致了相同的输出y

拉链法:

开一个一维数组来存储所有的哈希值,如果有出现冲突的话就在下面把数拉出来

数组a[] : 1 2 3 4 5 6 
           |
    	   23
    	   |
    	   13

开放寻址法:

开一个一维数组,开到数据范围的2-3倍

一般哈希算法模板:

拉链法

#include <cstring>
#include <iostream>

// N一般为质数,比如1e5 + 7 
// h[]是一条主链,e[]是主链下面拖的一条链
// e[]存放值,ne[]存放指针,idx表示用到了哪些位置
int h[N], e[N], ne[N], idx;

// 向哈希表中插入一个数
void insert(int x)
{
    int k = (x % mod + mod) % mod;
    // k是存放在h中的位置
    e[idx] = x;
    ne[idx] = h[k];
    // h[k]存放的是下面拖的第一个点的下标(类似于头指针)
    // 让新的点的next指针指向h[k],再让h[k]指向新的点
    h[k] = idx ++;
}

// 在哈希表中查询某个数是否存在
bool query(int x)
{
    int k = (x % mod + mod) % mod;
    for (int i = h[k]; i != -1; i = ne[i])
        if (e[i] == x)
            return true;

    return false;
}

// 链表头最开始初始化为-1
memset(h, -1, sizeof(h));

开放寻址法

const int null = 0x3f3f3f3f;
int h[N];	// 一般开到2-3倍
// 质数一般取刚好比N大的质数
// 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置
int query(int x)
{
    int t = (x % mod + mod) % mod;
    while (h[t] != null && h[t] != x)
    {
        t ++ ;
        if (t == N) t = 0;
    }
    return t;
}

memset(h, 0x3f, sizeof(h));
// memset按照字节去分配,而一个int为4字节,所以将0x3f3f3f3f拆分成0x3f
int k = query(x);
// 插入
h[k] = x;
// 查找
if(h[k] != null)	cout << "Yes" << endl;
else	cout << "No" << endl; 

字符串哈希

字符串前缀哈希法

Str = “ABCABCDEYXCAcwing”

预处理所有前缀的哈希

h[0] = 0

h[1] = "A"的哈希值

h[2] = "AB"的哈希值

  1. 把字符串看成是p进制的数

    ABCD

    (1234)p

    哈希值 = (1 * p^3 + 2 * p^2 + 3 * p^1 + 4 * p^0 ) mod q

  2. 不能映射成0

  3. 此处假定不存在冲突

可以利用前缀哈希求出所有子串的哈希值

计算子串 str[l ~ r] 的哈希值:

1         l-1 l           r
r-1	      l   l-1         0
    
h[1 - r] = h[r]
h[1 - l-1] = h[l-1] * p^(r-l+1)

l l l r r r这段的哈希值 h [ R ] − h [ L − 1 ] × p R − L + 1 h[R]-h[L-1]\times p^{R-L+1} h[R]h[L1]×pRL+1

用于判断两个子串是否相同

字符串哈希算法模板:

// 核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低
// 小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果

typedef unsigned long long ULL;
const int P = 13331, N = 1e5 + 10;
// const ULL q = pow(2,64);
ULL h[N], p[N]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
string str;

str = " " + str;
// 初始化
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
    h[i] = h[i - 1] * P + str[i];		// 计算前h位的哈希值
    p[i] = p[i - 1] * P;				// 计算p的指数次方
}

// 计算子串 str[l ~ r] 的哈希值
ULL get(int l, int r)
{
    return h[r] - h[l - 1] * p[r - l + 1];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

钰见梵星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值