一、哈希表

哈希表的常见作用是把一堆范围较大的数据映射到一个范围较小的集合(一般是1~N)中。这个映射叫作哈希函数(一般直接取模即可,但是可能有不同的数映射后的结果一样,这就是所谓的“冲突”。为了尽可能避免冲突,我们取模的对象应该为质数,并且离2的整数次幂尽可能远。按照冲突的处理方式,存储结构分为开放寻址法和拉链法。前面讲过的离散化是一种特殊的哈希函数)。哈希表的时间复杂度是O(1)。

1.存储结构

(1)拉链法

拉链法的原理是在把像看成一个个槽,在槽上挂一条链表存储对应于像的原像。如图(h表示哈希函数):
        下面我们来看看具体的代码实现吧!其中链表的建立见下图。如果你看了之后还不理解,可以参考XCPC第四站!链表+栈和队列+kmp一网打尽!

#include <iostream>
#include <cstring>
using namespace std;
//N取成这个数是因为它是比1e5大的最小的质数
const int N = 1e5+3;
//h数组表示“槽”,存储的是k这个虚拟头节点指向的节点的下标。e数组存储链表各个节点的值,h在这里不表示哈希函数。ne数组存储该节点下一个节点的编号,idx表示当前用到了哪个编号(即下一个节点的编号)
int h[N],e[N],ne[N],idx;
//链表的建立
void Insert(int x)
{//x%N后+N再%N是为了把x很小时(如-1e9)%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;scanf("%d",&n);//还记得吗?-1表示NULL。memset(h,-1,sizeof h);while(n--){char op[2];int x;scanf("%s%d",op,&x);if(op[0]=='I')   Insert(x);else{if(!find(x))    puts("No");else    puts("Yes");}}
}

(2)开放寻址法

开放寻址法只开一个一维数组而不需要链表。这个一维数组的经验长度一般为题目给的数据数量的2~3倍,这样冲突的概率较低。它的思路是:假设h(x1)=k(这里h表示哈希函数),又 恰巧有h(x2)=k,那么由于x1已经占了前面的槽位,就让x2从前面那个被x1占的槽位往后找槽位,直到找到一个没有被占的槽位。如图:

而查找的时候也是从第k个槽位开始,如果当前槽位有数且是x,那么就找到了;如果当前槽位有数但不是x,就往下一个槽位找;如果当前槽位没有数,说明x没找到。若要删除,则我们不会真的删除数据,而是会在要删除的数据上打一个标记。

#include<iostream>
using namespace std;
//null表示无穷大(一个比x的取值范围还要大很多的数)我们初始化的时候让h的各个元素都等于它,那么如果最后h[k]=null,说明这个位置没有数据
const int N = 200003,null = 0x3f3f3f;
//这里的h数组存的是真正的x的值。h[k]存储的是k这个合法位置的值。
int h[N];int find(int x)
{//由于我们开的槽位要比数据量大很多,因此绝对会有槽位没有被数据占据,因此循环一定能停止int k = (x%N+N)%N;//如果找到了,返回的是x的位置;如果没找到,返回的是x应该在(即可以在)的位置while(h[k]!=null&&h[k]!=x){k++;//如果k已经等于N,说明遍历到尽头,我们就让它重新开始。if(k==N)    k=0;}return k;
}int main()
{int n;scanf("%d",&n);for(int i = 0;i<N;i++)  h[i] = null;while(n--){int x;char op[2];scanf("%s%d",op,&x);int k = find(x);//直接在合法的位置插入x即可if(op[0]=='I')  h[k] = x;else{if(h[k]!=null)   puts("Yes");else    puts("No");}}
}

2.字符串哈希方式——字符串前缀哈希法

字符串的哈希方式分两步,在这两步的基础上,我们通过求出所有前缀和的哈希值就可以得到任意子串的哈希值。如图:

        那么我们如何定义某个字符串(或单个字母)的哈希值呢?第一步,我们把字符串的各个字母映射成非零的数字(为什么要求非零一会儿解释),如A->1,B->2,C->3,D->4,E->5。然后我们按照从高位到低位(左边是高位,右边是低位)的顺序写成p进制数据,即(12345)p。我们再把这个p进制的数转化成十进制的数。由于字符串可能很长,这个十进制的数可能很大,因此我们再对某个Q取模,这样最终得到的数就是哈希映射的像了。于是,不能把某个字母映射成0的原因就呼之欲出——如把A映射成0,那么A和AA和AAA……的映射结果是一样的,产生冲突。根据经验,当p取131或13331,Q等于2^64时,认为不会产生冲突。由于这个Q的经验值恰好是2 ^ 64,所以我们直接用unsigned long long存,溢出则相当于余数。那么如何由前缀和得知任意子串的哈希映射的像呢?假定我们知道了h[α0-α1)与h(α0-α2)(这里的α1和α2表示前缀和的终点字母,α0表示起始字母,且假定α1在α2前面),且α1和α2之间有k个字母,那么从α1后一个字母到α2这一段字符串的哈希值为h(α0-α2)-h(α0-α1)*p ^ (k+1)(这里读者试着举一个例子就能明白)。字符串哈希方法能够在O(1)的时间复杂度下判断两个字符串是否相同,比kmp还要厉害。

#include <iostream>
#include<cmath>
#include <cstdio>
#include <string>
using namespace std;
typedef unsigned long long ULL;const int N = 1e5+10;
const int P=131;
//h数组用来存储前缀哈希值,如h[k]表示前k个字母组成的字符串的哈希值。p[k]表示p进制下第k位在10进制下的值
ULL h[N],p[N];
//表示前x个字母组成的字符串的哈希值
ULL have(int x,int y)
{//这里不能用p的幂乘,因为我们需要自动取模!!return h[y]-h[x-1]*p[y-x+1];
}int main()
{int n,m;scanf("%d%d",&n,&m);char str[N];//这里必须要+1!因为根据题目,字符编号要从1开始!scanf("%s",str+1);p[0] = 1;for(int i = 1;i<=n;i++){h[i] = h[i-1]*P+str[i];p[i] = p[i-1]*P;}while(m--){int l1,r1,l2,r2;scanf("%d%d%d%d",&l1,&r1,&l2,&r2);if(have(l1,r1)==have(l2,r2))  puts("Yes");else    puts("No");}return 0;
}

以上就是本篇文章的全部内容啦!如果你觉得对自己有帮助,请多多支持博主,给予博主日更的动力哦!

XCPC第七站!带你学懂哈希表!相关推荐

  1. 七十五、Python | Leetcode哈希表系列

    @Author:Runsen @Date:2020/7/3 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

  2. python leetcode_七十五、Python | Leetcode哈希表系列

    @Author:Runsen @Date:2020/7/3 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

  3. 哈希吧,滚雪球学 Python 哈希表与可哈希对象

    橡皮擦,一个逗趣的互联网高级网虫,新的系列,让我们一起 Be More Pythonic. 滚雪球学 Python 第二轮 已完成的文章清单 十一.Python 哈希表与可哈希对象 11.1 哈希表( ...

  4. Android JetPack架构篇,一个实战项目带你学懂JetPack

    第五届世界互联网大会昨日开幕,来自76个国家的1500余位嘉宾出席大会.腾讯公司董事会主席兼首席执行官马化腾在大会开幕式演讲中表示,全球产业都在进行数字化,在此期间机遇挑战并存,产业互联网机会巨大. ...

  5. 5分钟带你学懂ROC曲线

    目录 ROC 混淆矩阵: 定义 生成ROC曲线 生成实例 AUC(Area under ROC Curve) AUC对模型性能简单评估 ROC曲线的优势 ROC ROC全称是受试者工作特征(Recei ...

  6. 一篇文章带你学懂C++虚函数表的继承问题

    虚函数表-继承 单继承 结论: 当父类定义了虚函数时,在子类进行继承的时候会将父类的虚函数表也给继承下来所以那一些虚函数在子类中也是virtual类型的,如果要对父类中的虚函数进行重写时或添加虚函数, ...

  7. 一篇文章带你学懂Redis

    1 是什么 Redis:REmote DIctionary Server(远程字典服务器) 是完全开源免费的,用C语言编写的,遵守BSD协议, 是一个高性能的(key/value)分布式内存数据库,基 ...

  8. 数据结构知识点总结_大牛带你学 | 考研数据结构中线性表中顺序结构的知识点总结...

    前言 我们都知道,数据结构中逻辑结构可以划分为线性结构(线性表)与非线性结构两大类. 而存储结构指的是数据元素在计算机中的存储及其逻辑关系的表现,也就是在计算机当中对逻辑结构的表示. 线性表的存储结构 ...

  9. hashmap是散列表吗_一篇文章教你读懂哈希表-HashMap

    题图Pid=68670770 在最近的学习过程中,发现身边很多朋友对哈希表的原理和应用场景不甚了解,处于会用但不知道什么时候该用的状态,所以我找出了刚学习Java时写的HashMap实现,并以此为基础 ...

最新文章

  1. centos 修改shm
  2. 小白学python买什么书-书单狗 篇一:小白学Python,到底要看多少书?
  3. HTML5 跨文档消息传输
  4. vue中什么样的数据可以是在视图中显示
  5. Boost:bind绑定全局占位符的测试程序
  6. 两根硬铜线并线接插座_高级电工原来都这样接电线,手法还没见过,我也立马学...
  7. 利用angular4和nodejs-express构建一个简单的网站(六)—用户模块和路由分析
  8. [转]Android Activity和Intent机制学习笔记
  9. Qt文档阅读笔记-关于Qt Core的进一步认识
  10. JavaScript基础函数体中的唯一var模式(002)
  11. Java5~11新特性
  12. 计算机栏和用户栏有啥区别,任务栏与桌面的区别是
  13. python类似turtle的库_Python库——turtle
  14. 后悔当初没考研。。。
  15. win10 Java 环境配置
  16. iPhone/iPad/Touch苹果设备型号对应名称表
  17. ros源码下载及编译
  18. 查尔姆斯理工计算机教授,瑞典查尔姆斯理工大学王二刚教授系统评述:供体-受体型三元共轭聚合物实现高效太阳能电池器件构筑...
  19. Java并发编程 - 共享模型之管程
  20. ​ECCV 2022 | 清华腾讯AI Lab提出REALY: 重新思考3D人脸重建的评估方法

热门文章

  1. [转载]明星的真实年龄
  2. PyCharm创建新项目:Python解释器配置
  3. linux重定向logcat,logcat重定向adb命令.doc
  4. 官宣:3月27日的PMP考试,确定延期至6月、7月
  5. 开放数据库:共享杯版_前列腺肿瘤预警数据集
  6. 随便聊聊水面效果的2D实现(一)
  7. VS2019 C++的跨平台开发——C# WPF
  8. 南通理工学院计算机房的女老师,【南通网】毕业不离校,南通理工学院这个女生干啥呢...
  9. MobTech ShareSDK iOS端快速集成
  10. 《中学语文》期刊简介及投稿要求