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

其中前边说到的离散化也是一种特殊的哈希方式,只不过离散化注重保序性,因此使用二分查找的方法。

其中存在问题就是:可能会把不同的数映射成相同的数,这就是哈希冲突,则我们处理冲突的方法就是将一组关键字映射到一个有限的连续的地址集(区间)上,到时候我们查找的时候就可以顺着这个地址依次查找。

而处理冲突的两种方法:拉链法开放寻址法

拉链法:

这种方法就是把映射值相同的点像链表一样挂在同一个地址上,当我们寻找的时候就可以通过地址来直接索引。

而寻找这个地址或者说映射的方法就是取模(mod),而mod的数最好就是大于映射范围的第一个质数,这样会更大的减少冲突(数学推理不清楚,听大佬说的)。

取模的方法: k = (x % N + N) % N (N是映射后的范围,这样取模是为了防止负数)

开放寻址法

这种方法只需要开一个数组,不过这个数组的大小最好是映射后范围的2~3倍,那是因为这种方法再寻找映射后结果如果被占用则它顺着这个结果继续向下找直到找到空位。

说的形象一点就好比上厕所:这个坑位有人,咱就必须取下一个坑位,直到找到一个空的坑位。

例题

模拟散列表

维护一个集合,支持如下几种操作:

“I x”,插入一个数x;

“Q x”,询问数x是否在集合中出现过;

现在要进行N次操作,对于每个询问操作输出对应的结果。

输入格式

第一行包含整数N,表示操作数量。

接下来N行,每行包含一个操作指令,操作指令为”I x”,”Q x”中的一种。

输出格式

对于每个询问指令“Q x”,输出一个询问结果,如果x在集合中出现过,则输出“Yes”,否则输出“No”。

每个结果占一行。

数据范围

1 ≤ N ≤ 10^5

−10^9 ≤ x ≤ 10^9

输入样例:

5

I 1

I 2

I 3

Q 2

Q 5

输出样例:

Yes

No

拉链法Code:

#include <iostream>
#include <cstring> using namespace std; const int N = 100003; //寻找一个大于映射范围的第一个质数 最好用质数取模
int e[N], ne[N], idx, h[N]; void insert(int x)
{
int k = (x % N + N) % N;
e[idx] = x;
ne[idx] = h[k];
h[k] = idx;
idx++;
} bool query(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); memset(h, -1, sizeof(h)); while(n--)
{
int x;
char op[2];
scanf("%s%d", op, &x);
if(*op == 'I') insert(x);
else
{
if(query(x)) printf("Yes\n");
else printf("No\n");
}
} system("pause");
return 0;
}

开放寻址法Code:

#include <iostream>
#include <cstring> using namespace std; const int N = 200003, null = 0x3f3f3f3f; //数组开到个数上限的2~3倍, null表示为空
int h[N]; int finds(int x) //两个作用:1.寻找可以插入的位置 2.寻找哈希表中是否存在要查找的数字
{
int t = (x % N + N) % N;
while(h[t] != null && h[t] != x)
{
t++;
if(t == N) t = 0; //如果找到尾则从头寻找
} return t;
} int main()
{
memset(h, 0x3f, sizeof(h)); //寻找一个标志 这个标志大于x的范围 int n;
scanf("%d", &n); while(n--)
{
char op[2];
int x; scanf("%s%d", op, &x);
if(*op == 'I') h[finds(x)] = x;
else
{
if(h[finds(x)] != null) puts("Yes");
else puts("No");
}
} return 0;
}

字符串哈希

字符串哈希就是把一个字符串哈希为整数,具体方法就是把一个字符串具体看成一个P进制数(P不确定),然后我们把他换算成十进制数字,这样就可以直接通过数字来判断两个字符串是否相等。(相当厉害并且好用的一种方法)。

给定一个长度为n的字符串,再给定m个询问,每个询问包含四个整数l1,r1,l2,r2,请你判断[l1,r1]和[l2,r2]这两个区间所包含的字符串子串是否完全相同。

字符串中只包含大小写英文字母和数字。

输入格式

第一行包含整数n和m,表示字符串长度和询问次数。

第二行包含一个长度为n的字符串,字符串中只包含大小写英文字母和数字。

接下来m行,每行包含四个整数l1,r1,l2,r2,表示一次询问所涉及的两个区间。

注意,字符串的位置从1开始编号。

输出格式

对于每个询问输出一个结果,如果两个字符串子串完全相同则输出“Yes”,否则输出“No”。

每个结果占一行。

数据范围

1 ≤ n,m ≤ 10^5

输入样例:

8 3

aabbaabb

1 3 5 7

1 3 6 8

1 2 1 2

输出样例:

Yes

No

Yes

思路:直接把每个字符串的哈希值存入一个数组,然后对数组排序后进行判断即可。

进制P,一般取为131,1331..(同样有数学证明)。

字符串换算为10进制范围会很大,所以我们使用 unsigned long long ,溢出会自动对2^64取模

代码:

 1 #include <iostream>
2 #include <algorithm>
3 #include <cstring>
4 #include <cstdio>
5
6 using namespace std;
7
8 typedef unsigned long long ULL;
9
10 const int N = 10010, P = 131;
11 char str[N];
12 ULL a[N];
13
14 ULL hashGet(char str[])
15 {
16 int len = strlen(str);
17 ULL res = 0;
18 for(int i = 0; i < len; i++) //将P进制换算为10进制
19 {
20 res = res*P + str[i];
21 }
22
23 return res;
24 }
25
26 int main()
27 {
28 int n, res = 1;
29
30 scanf("%d", &n);
31 for(int i = 0; i < n; i++)
32 {
33 scanf("%s", str);
34 a[i] = hashGet(str);
35 }
36
37 sort(a, a+n);
38
39 for(int i = 1; i < n; i++)
40 {
41 if(a[i] != a[i-1]) res++;
42 }
43
44 printf("%d\n", res);
45
46 return 0;
47 }

字符串前缀哈希法

前缀和与字符串哈希相结合,可以直接判断出一个字符串中某两段字符串是否相等,不再需要用kmp。

S = "ABCDEFG"

h[0] = 0

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

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

h[3] = "ABC" 的哈希值

h[4] = "ABCD" 的哈希值

.......

利用前缀哈希,就可以计算出所有字符串字段的哈希值,如图求L~R的哈希值,我们已经有了hash[R]hash[L-1]的哈希值。

同样把字符串看作P进制数,则左边就是高位 右边就是低位,要求出L~R的哈希值我们要做的就是把h[L-1]的P进制左移与R的高位对齐,相减即可。

假设进制P为2:两个字符串分别是,求hash

现在要做的就是把2左移两位,然后相减,得出hash,所以这里的P进制数也是这个道理。

L~R计算哈希值计算公式:\(h[R] - h[L-1]*P^{L-R+1}\)

所以再判断两个字串是否相等时可以直接通过两个字符串的左右边界得出两个字符串的哈希值来判断。

例题

给定一个长度为n的字符串,再给定m个询问,每个询问包含四个整数l1,r1,l2,r2,请你判断[l1,r1]和[l2,r2]这两个区间所包含的字符串子串是否完全相同。

字符串中只包含大小写英文字母和数字。

输入格式

第一行包含整数n和m,表示字符串长度和询问次数。

第二行包含一个长度为n的字符串,字符串中只包含大小写英文字母和数字。

接下来m行,每行包含四个整数l1,r1,l2,r2,表示一次询问所涉及的两个区间。

注意,字符串的位置从1开始编号。

输出格式

对于每个询问输出一个结果,如果两个字符串子串完全相同则输出“Yes”,否则输出“No”。

每个结果占一行。

数据范围

1 ≤ n,m ≤ 10^5

输入样例:

8 3

aabbaabb

1 3 5 7

1 3 6 8

1 2 1 2

输出样例:

Yes

No

Yes

#include <iostream>

using namespace std;

typedef unsigned long long ULL;

const int N = 100010, P = 131;
int p[N], h[N];
char str[N]; ULL get(int l, int r)
{
return h[r] - h[l-1]*p[r-l+1];
} int main()
{
int n, m;
scanf("%d%d", &n, &m);
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(get(l1, r1) == get(l2, r2)) puts("Yes");
else puts("No");
} system("pause");
return 0;
}

哈希表(hash)的更多相关文章

  1. 算法与数据结构基础 - 哈希表(Hash Table)

    Hash Table基础 哈希表(Hash Table)是常用的数据结构,其运用哈希函数(hash function)实现映射,内部使用开放定址.拉链法等方式解决哈希冲突,使得读写时间复杂度平均为O( ...

  2. 哈希表(Hash)的应用

    $hs=@() #定义数组 $hs=@{} #定义Hash表,使用哈希表的键可以直接访问对应的值,如 $hs["王五"] 或者 $hs.王五 的值为 75 $hs=@''@ #定义 ...

  3. PHP关联数组和哈希表(hash table) 未指定

    PHP有数据的一个非常重要的一类,就是关联数组.又称为哈希表(hash table),是一种很好用的数据结构. 在程序中.我们可能会遇到须要消重的问题,举一个最简单的模型: 有一份username列表 ...

  4. (四)Redis哈希表Hash操作

    Hash全部命令如下: hset key field value # 将哈希表key中的字段field的值设为value hget key field # 返回哈希表key中的字段field的值val ...

  5. 词典(二) 哈希表(Hash table)

    散列表(hashtable)是一种高效的词典结构,可以在期望的常数时间内实现对词典的所有接口的操作.散列完全摒弃了关键码有序的条件,所以可以突破CBA式算法的复杂度界限. 散列表 逻辑上,有一系列可以 ...

  6. 数据结构,哈希表hash设计实验

    数据结构实验,hash表 采用链地址法处理hash冲突 代码全部自己写,转载请留本文连接, 附上代码 #include<stdlib.h> #include<stdio.h> ...

  7. Redis原理再学习04:数据结构-哈希表hash表(dict字典)

    哈希函数简介 哈希函数(hash function),又叫散列函数,哈希算法.散列函数把数据"压缩"成摘要,有的也叫"指纹",它使数据量变小且数据格式大小也固定 ...

  8. 什么叫哈希表(Hash Table)

    散列表(也叫哈希表),是根据关键码值直接进行访问的数据结构,也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表. - 数据结构 ...

  9. 数据结构 哈希表(Hash Table)_哈希概述

    哈希表支持一种最有效的检索方法:散列. 从根来上说,一个哈希表包含一个数组,通过特殊的索引值(键)来访问数组中的元素. 哈希表的主要思想是通过一个哈希函数,在所有可能的键与槽位之间建立一张映射表.哈希 ...

  10. golang数据结构之散哈希表(Hash)

    hash.go package hash import ( "fmt" ) type Emp struct { ID int Name string Next *Emp } //第 ...

随机推荐

  1. python 镜像

    https://pypi.douban.com/simple/   豆瓣源 pip install -i https://pypi.douban.com/simple/ pymysql # pymys ...

  2. Windows安装hexo并配置nginx

    前言 Hexo是一款基于NodeJS的静态博客框架,依赖少且易于安装使用,可以方便地生成静态网页. 本文记录Windows安装hexo,配置第三方主题Fluid,并配置nginx的全过程. nodej ...

  3. Oracle内存管理方式介绍(转载)

    "Oracle内存管理可分为两大类,自动内存管理和手动内存管理.其中手动内存管理又可分为自动共享内存管理,手动共享内存管理,自动PGA内存管理以及手动PGA内存管理.本文会简单的介绍不同的内 ...

  4. ATtiny88初体验(三):串口

    ATtiny88初体验(三):串口 ATtiny88单片机不包含串口模块,因此只能使用软件方式模拟串口时序. 串口通信时序通常由起始位.数据位.校验位和停止位四个部分组成,常见的配置为1位起始位.8位 ...

  5. Jmeter逻辑控制器Switch Controller的用法

    一.概述 类似编程语言中的switch函数,Switch Controller根据给定的值n(可使用变量)选择执行其下的 第n+1个子节点. 作用:Switch Controller通过给该控制器中的 ...

  6. 想了解API接口,这一篇就够了

    API(Application Programming Interface)接口,对于大多数人来说可能还比较陌生,但实际上我们每天都在与它打交道.无论是使用手机上的应用程序,还是在网上购物,都少不了A ...

  7. 现代 CSS 解决方案:数学函数 Round

    在 CSS 中,存在许多数学函数,这些函数能够通过简单的计算操作来生成某些属性值,例如在现代 CSS 解决方案:CSS 数学函数一文中,我们详细介绍了 calc():用于计算任意长度.百分比或数值型数 ...

  8. ViTPose+:迈向通用身体姿态估计的视觉Transformer基础模型

    身体姿态估计旨在识别出给定图像中人或者动物实例身体的关键点,除了典型的身体骨骼关键点,还可以包括手.脚.脸部等关键点,是计算机视觉领域的基本任务之一.目前,视觉transformer已经在识别.检测. ...

  9. 深入分布式一致性:Raft 和 etcdRaft

    分布式一致性是构建可靠的分布式系统的关键要素之一.为了确保数据的一致性和可用性,一致性算法的设计变得至关重要.在这篇博文中,我们将深入探讨两个与分布式一致性密切相关的主题:Raft 算法和 etcdR ...

  10. 4399 Flash游戏专用浏览器, 无需安装Flash插件

    目前所有的主流浏览器都已经不再支持Flash了,即使有一些国内浏览器还支持flash,但只能安装国内特供版Flash Player. 但问题的关键在于,这个国内特供版跟 Adobe 海外发行的版本是两 ...