哈希

哈希 (蒟蒻篇)

哈希是什么呢?

先看一眼百度解释(非人话)

散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。

反正以我语文不及格的水平是啥也读不懂。简单的知识点就是:哈希表是一种数据结构

哈希的主要特点就是:可以根据key来直接访问数据,所以查询较快

哈希函数

哈希函数y = F(x) ,一般选用一下三种方式构造:

(1)直接定址法:

H(key) = key 或 H(key) = a × key + b,式中a和b是常数。此方式计算最简单且不会产生冲突,适合关键字的分布基本连续,若分布不连续,则会造成存储空间的大量浪费。

(2)除留余数法:

最简单最常用的方法,假定散列表表长为m,取一个不大于m但最接近或等于m的质数p,同时离2的整次幂尽可能的远,因为这样子取值其冲突概率会最小。
H(key) = key %p

但此时也产生了一个问题,不同的数取模可能会得到一个相同的结果,从而导致值域和定义域不是一对一的关系,而变成了一对多的关系,使其不能通过哈希函数来唯一确定一个值,将这种现象称为冲突

(3)数字分析法:

设关键字是r进制数,而r个数码在各位上出现的频率不一定相同,可能在某些位上分布均匀一些,每种数码出现的机会均等;而在某些位上分布不均匀,只有某几种数码经常出现,此时应选取数码分布较为均匀的若干位作为散列地址。此方法适用于已知的关键字集合,若更换了关键字,则需要重新构造新的哈希函数。

解决哈希冲突

有两种方法

第一种:拉链法

假设有十个数 1 2 3 4 5 6 7 8 9 10 ,我们设mod为3
image

有两个拉链法的写法,这两个方法都可以,二维数组写起来更方便,链式前向星更省空间,但是不容易估计范围,我总是RE

链式前向星

康康代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e5+3;
int e[N],ne[N],p[N],idx;
void insert (int k)
{
    int t = (k%N+N)%N;
    e[idx]=k;
    ne[idx]=p[t];
    p[t]=idx++;
}
bool find (int k)
{
    int t = (k%N+N)%N;
    for (int i=p[t];i!=-1;i=ne[i])
    {
        if (e[i]==k)
        return true;
    }
    return false;
}
int main ()
{
    int n;
    cin>>n;
    memset (p,-1,sizeof (p));
    while (n--)
    {
        char op[2];
        int k;
        scanf ("%s%d",op,&k);
        if (op[0]=='I')     insert(k);
        else 
        {
            if (find (k))
            puts("Yes");
            else
            puts("No");
        }
    }
    return 0;
}

动态二维数组

康康代码
#include <iostream>
#include <vector>
using namespace std;
vector<int>p[100003];
const int mod=100003;
void insert(int x)
{
    int t=(x%mod+mod)%mod;
    p[t].push_back(x);
}
bool find(int x)
{
    int t=(x%mod+mod)%mod;
    for (int i=0;i<p[t].size();i++)
    {
        if (p[t][i]==x) return true;
    }
    return false;
}
int main ()
{
    int n;
    scanf ("%d",&n);
    for (int i=0;i<n;i++)
    {
        char s;
        int k;
        getchar ();
        scanf("%c %d",&s,&k);
        if (s=='I')
        {
            insert(k);
        }
        else
        {
            if (find(k))    puts("Yes");
            else puts("No");
        }
    }
}

第二种方法:开放寻址法

对于增量序列,分为线性探测法和平方探测法:

[1] 线性探测法: 简单来说,若发生冲突则顺序查看表中下一个单元,当查找到表尾地址时,再从表头地址开始查找。直到找到一个空闲地址或查边全表。当具有较多同义词时,会发生 “聚集”(或堆积) 现象,大大降低查找效率。

[2] 平方探索法: 查找跳跃长度为线性整数的平方,即0 、 1 、-1 、 (2)^2 、-(2)^2 、3^2 、-(3)^2…。该方法可以使表中存的元素相对均匀的分布,避免了堆积现象。

我这里是线性探测法

康康代码

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 2e5+3,null = 0x3f3f3f3f;
int p[N];
int find (int k)
{
    int t=(k%N+N)%N;
    while (p[t]!=null&&p[t]!=k)
    {
        t++;
        if (t==N)   t=0;
    }
    return t;
}
int main ()
{
    int n;
    cin>>n;
    memset (p,null,sizeof (p));
    while (n--)
    {
        char op[2];
        scanf ("%s",op);
        int x;
        scanf ("%d",&x);
        int k=find (x);
        if (op[0]=='I')
        p[k]=x;
        else
        {
            if (p[k]!=null)
            puts("Yes");
            else
            puts("No");
        }
    }
    return 0;
}

字符串哈希

那么字符串Hash,其实就是:构造一个数字使之唯一代表一个字符串。但是为了将映射关系进行一一对应,也就是,一个字符串对应一个数字,那么一个数字也对应一个字符串。

用字符串Hash的目的是,我们如果要比较一个字符串,我们不直接比较字符串,而是比较它对应映射的数字,这样子就知道两个“子串”是否相等。从而达到,子串的Hash值的时间为 O(1) ,进而可以利用“空间换时间”来节省时间复杂的。

acwing 字符串哈希
我这里用的是p进制来进行哈希

康康代码吧
#include <iostream>
using namespace std;
#define ULL unsigned long long
ULL p[100010],h[100010],P = 131;
ULL get(int l1,int r1)
{
    return h[r1]-h[l1-1]*p[r1-l1+1];
}
int main ()
{
    int a,b;
    cin>>a>>b;
    string s;
    cin>>s;
    p[0]=1;
    for (int i=1;i<=s.size();i++)
    {
        h[i]=h[i-1]*P+s[i-1];
        p[i]=p[i-1]*P;
    }
    while (b--)
    {
        int l1,r1,l2,r2;
        cin>>l1>>r1>>l2>>r2;
        if (get(l1,r1)==get(l2,r2)) puts("Yes");
        else puts("No");
    }
}

例题
洛谷 字符串哈希
两个都是板子题,有一点不一样,但是都很好想

STL

C++的容器unordered_map 内部就是哈希表,具有快速检索的功能

非常的方便,但是如果想练习手写Hash,还是先别用stl。

题单

字符串哈希我能做的就找到了这一个
Barn Echoes G

散列表

SDUTOJ题单

posted @ 2022-07-14 17:08  Seaside_G  阅读(115)  评论(1)    收藏  举报