数据结构 第六章 查找(二) 散列表(Hash),KMP

本文深入探讨了哈希表中的负载因子概念,介绍了多种哈希函数的实现,如除余法、乘余取整法等,并详细阐述了开散列(拉链法)和闭散列(开放寻址法)解决冲突的策略,包括线性探查、二次探查和随机探查。同时,提到了KMP算法在字符串匹配中的应用,讨论了其如何利用最长公共前后缀避免不必要的比较。最后,总结了散列表中解决冲突的两种主要方法:开放地址法和链地址法。

1.负载因子

负载因子=已有元素数组长度\frac{已有元素}{数组长度}

2.哈希函数

[1] 除余法h(x) = x % M
[2] 乘余取整法 h(x) = floor(n * (A * x的小数部分))
[3] 平方取中法 先平方,然后取中间几位
[4] 基数转换法 换成其他进制,然后取其中几位
[5] ELFhash字符串

2种哈希算法

1.开散列方法(拉链法)
2.闭散列方法(开放寻址法)
在这里插入图片描述

聚集和二级聚集

a. 线性探查法 d(i) = (d(0) + i * c) % M。易产生聚集问题。
b. 二次探查法。易产生二级聚集问题。
d(2i - 1) = (d(0) + i^2) % M
d(2i) = (d(0) - d ^2) % M
c. 随机探查法。易产生二级聚集问题。
d. 双散列探查法

拉链法
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e5+3;
int n,x,idx;
char op[2];
int h[N],e[N],ne[N];
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
bool check(int t,int x)
{
    for(int i=h[t];~i;i=ne[i])
    {
        if(e[i]==x) return true;
    }
    return false;
}
int main()
{
    memset(h,-1,sizeof h);
    scanf("%d", &n);
    for(int i=0;i<n;i++)
    {
        scanf("%s",op);
        if(*op=='I'){
            scanf("%d",&x);
            int t=(x%N+N)%N;
            if(!check(t,x))
                add(t,x);
        }
        else{
            scanf("%d",&x);
            int t=(x%N+N)%N;
            if(check(t,x)) puts("Yes");
            else puts("No");
        }
    }
    
    return 0;
}
开放寻址法
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e5+3,INF=0x3f3f3f3f;
int n,x,idx;
char op[2];
int h[N];
int find(int x)
{
    int t=(x%N+N)%N;
    while(h[t]!=INF && h[t]!=x)
        t=(t+1)%N;
    return t;
}
int main()
{
    memset(h,0x3f,sizeof h);
    scanf("%d", &n);
    for(int i=0;i<n;i++)
    {
        scanf("%s%d",op,&x);
        if(*op=='I')
        {
            h[find(x)]=x;
        }
        else{
            if(h[find(x)]==INF) puts("No");
            else puts("Yes");
        }
    }
    return 0;
}

3.KMP算法

ne[i]:前i个字母构成的字符串中,最长的前缀相等的后缀长度
在这里插入图片描述

KMP算法代码
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010, M = 1000010;

int n, m;
char p[N], s[M];
int ne[N];

int main()
{
    scanf("%d%s", &n, p + 1);
    scanf("%d%s", &m, s + 1);
	
	//求ne数组
    for (int i = 2, j = 0; i <= n; i ++ )
    {
        while (j && p[i] != p[j + 1]) j = ne[j];
        if (p[i] == p[j + 1]) j ++ ;
        ne[i] = j;
    }

	//求匹配情况
    for (int i = 1, j = 0; i <= m; i ++ )
    {
        while (j && s[i] != p[j + 1]) j = ne[j];
        if (s[i] == p[j + 1]) j ++ ;
        if (j == n) printf("%d ", i - n);
    }
    return 0;
}

散列表中解决冲突的两种方法是(开放地址法)和(链地址法)。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Jay_fearless

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

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

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

打赏作者

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

抵扣说明:

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

余额充值