Hash(哈希(字符串哈希))模板和做题总结(详细易懂)

前言:

在学习本节课之前,请大家思考这样一个问题:如果我们要在一个长度为 的随机整数序列 中统计每 个数出现的次数,可以用什么方法?

不难想到,我们可以建立一个数组 ,然后将整数序列A中的元素映射到数组的下标,最后遍历整个数组 即可统计出随机整数序列 中每个数出现的次数。但是如果数列 中数比较大呢?我们就可以借助Hash 表来解决这个问题。

一 Hash表

散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存储存位置的数据结构。也就是说,它 通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度 当我们要对若干复杂信息进行统计时,可以用Hash函数把这些复杂信息映射到一个容易维护的值域内。

比如刚才那个问题,当N过于大时,我们可以设计Hash函数为 ,其中P是 一个较大的质数,但不超过N,这样这个Hash函数就把数列 中的值映射到了不同的区域进行储存,然 后继续进行后面的操作。

总的来说, 其基本原理是:使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函 数,也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标,hash值)相对应,于是 用这个数组单元来存储这个元素。

但是,由于值域变简单、范围变小,有可能造成两个不同的原始信息被Hash函数映射为相同的值,这种 情况叫做Hash冲突,我们下面会讲解如何处理这种冲突。

总结一下,Hash表主要包括两个基本操作:

1.计算Hash函数的值

2.定位到对应链表中依次遍历、比较。

1 Hash函数的构造

hash函数构造原则

1.简答易算

2.能够使地址均匀分布

直接定址法 H(key)=a*key+b

除留余数法 H(key)=key MOD p,p<=m (最简单,最常用)p的选取尽量是一个质数,并且离2的 整次幂尽可能的远

2 拉链法处理hash冲突模板

拉链法是借助邻接表实现的,当目标元素经过Hash函数计算后所得到的值与之前的元素计算得出的值相 同,就将其插入以这个Hash值开头的链表中去。

#include <cstring>
#include <iostream>

using namespace std;

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

//* 开一个槽 h
int h[N], e[N], ne[N], idx;  //邻接表
//分别存储链表头,数值,指向的下一元素编号
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) {//查找
    //用上面同样的 Hash函数 讲x映射到 从 0-1e5 之间的数
    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 n;

int main() {
    cin >> n;

    memset(h, -1, sizeof h);  //将槽先清空 空指针一般用 -1 来表示

    while (n--) {
        string op;
        int x;
        cin >> op >> x;
        if (op == "I") {
            insert(x);
        } else {
            if (find(x)) {
                puts("Yes");
            } else {
                puts("No");
            }
        }
    }
    return 0;
}

3 开放寻址法处理hash冲突

开放寻址法指的是当你发现这个位置已经被占用后,就按照一定的喜好去寻找没有被占用的位置。寻找 新的位置的方法

双重散列法

双重散列是用于开放寻址法的最好方法之一,所谓双重散列,意思就是不仅要使用一个散列函数。我们 需要使用两个散列函数 , .我们先用第一个散列函数,如果发生冲突,则再利 用第二个散列函数。

线性勘测法

一旦出现冲突后,就尝试后面邻接的位置,直到找到没有被占用的位置为止。但是这种方法虽然解决了 当前的冲突,但可能会造成其他冲突,出现数据聚集现象

int h[N];
// 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置
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;
}

4(例题)、雪花雪花

有 N 片雪花,每片雪花由六个角组成,每个角都有长度。

第 i 片雪花六个角的长度从某个角开始顺时针依次记为 ai,1,ai,2,…,ai,6。

因为雪花的形状是封闭的环形,所以从任何一个角开始顺时针或逆时针往后记录长度,得到的六元组都代表形状相同的雪花。

例如 ai,1,ai,2,…,ai,6和 ai,2,ai,3,…,ai,6,ai,1 就是形状相同的雪花。

ai,1,ai,2,…,ai,6 和 ai,6,ai,5,…,ai,1 也是形状相同的雪花。

我们称两片雪花形状相同,当且仅当它们各自从某一角开始顺时针或逆时针记录长度,能得到两个相同的六元组。

求这 N 片雪花中是否存在两片形状相同的雪花。

输入格式

第一行输入一个整数 N,代表雪花的数量。

接下来 N 行,每行描述一片雪花。

每行包含 6 个整数,分别代表雪花的六个角的长度(这六个数即为从雪花的随机一个角顺时针或逆时针记录长度得到)。

同行数值之间,用空格隔开。

输出格式

如果不存在两片形状相同的雪花,则输出:

No two snowflakes are alike.

如果存在两片形状相同的雪花,则输出:

Twin snowflakes found.

数据范围

1≤N≤100000,
0≤ai,j<10000000

输入样例:

2
1 2 3 4 5 6
4 3 2 1 6 5

输出样例:

Twin snowflakes found.
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值