首页 > 其他分享 >Acwing 840. 模拟散列表

Acwing 840. 模拟散列表

时间:2023-12-08 18:14:46浏览次数:36  
标签:key 840 int 函数 列表 关键字 地址 哈希 Acwing

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

  1. I x,插入一个整数 \(x\);
  2. Q x,询问整数 \(x\) 是否在集合中出现过

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

原题链接:840. 模拟散列表 - AcWing题库

哈希表[1]

基本概念

哈希表也叫散列表,通过将键映射到索引位置(在关键字和位置之间建立对应关系)来实现高效的查找操作
记作 \(Loc(i)=Hash(key_i)\) 。

时间复杂度:\(O(1)\)
无论哈希表中有多少条数据,其插入和查找操作的时间复杂度都恒定。
因为哈希表的查找速度非常快,所以在很多程序中都有使用哈希表,例如拼音检查器。

基本思想:转换思想,通过哈希函数将非int的键或者关键字转换成int数组下标。
注意:并不是每个元素都需要通过哈希函数来将其转换成数组下标,有些可以直接作为数组的下标。

缺点:基于数组,空间效率较低,扩容成本较高;
当哈希表被填满时,会造成比较严重的性能下降。

索引存储散列存储的区别:散列存储多了 \(hash\) 函数运算的过程,用于节省空间

举例:用哈希表来存放班级里面学生信息
利用学号作为关键字时,因为数据类型为int,可以同时作为数据下标,不需要通过哈希函数进行转化;
但如果需要将姓名作为关键字,就需要哈希函数完成键值的重映射。

常见的哈希函数[2]

构造准则:简单、均匀、冲突最小
构造哈希函数的目标是使得到的 \(n\) 个元素的哈希地址尽可能均匀地分布在 \(m\) 个连续内存单元地址上,同时使计算过程尽可能简单以达到尽可能高的时间效率[3]

1. 线性定址法

直接取关键字的某个线性函数作为存储地址:\(Hash(key)=a×key+b\)
其中 \(a\) 和 \(b\) 为常数,这种哈希函数叫做自身函数。当 \(a=1\),\(b=0\) 时,\(H(key)=key\)

优点:直接定址所得地址集合和关键字集合的大小相同;对于不同的关键字不会产生冲突。
缺点:空间效率低,占用连续地址空间,需要提前确定关键字的取值范围,且不能太大。

举例:有一个从1岁到100岁的人口统计表,其中,年龄作为关键字,哈希函数取其自身,即哈希函数为 \(H(key)= key\)。这样,若要询问25岁的人有多少,则只要查表中地址为25的桶即可。

2. 除留余数法

将关键字对某一小于散列表长度的数 \(p\) (\(p<a.length\)) 取余的结果作为存储地址:\(Hash(key)=key\%p\)
最简单,也最常用。不仅可以对关键字直接取模,也可在对关键字进行折迭、平方取中等运算之后取模。
对 \(p\) 的选择很重要,一般情况下选 \(p\) 为质数不包含小于 \(20\) 的质因素的合数

3. 数字分析法

取某关键字的某几位组合成哈希地址。
假设已经知道哈希表中所有的关键字值,而且关键字值都是数字
所选的位应当是:各种符号在该位上出现的频率大致相同

举例:有1000个记录,关键字为10位十进制整数\(x_1、x_2…x_{10}\),如哈希表长度为2000。
假设经过分析,各关键字中 \(x_3\)、\(x_5\) 和 \(x_7\) 的取值分布近似随机,则可取哈希函数为:\(h(key)=h(x_1、x_2…x_{10})=x_3x_5x_7\)。
例如,\(h(3778597189)=757\),\(h(9166372560)=632\)。

4. 平方取中法

对关键字取平方,然后将得到结果的中间几位作为存储地址;
当无法确定关键字中哪几位分布较均匀时,先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。

特点:通过平方扩大差别,另外,中间几位与关键字中的每一位都相关,故不同关键字会以较高的概率产生不同的、均匀的哈希地址。

5. 折叠法

将关键字自左到右分成位数相等的几部分,最后一部分可以短些;
然后将这几部分叠加求和,并按哈希表表长,取后几位作为哈希地址。
适用于:每一位上各符号出现概率大致相同的情况。

举例:根据国际标准图书编号(ISBN)建立一个哈希表。
如一个国际标准图书编号 \(0-442-20586-4\) 的哈希地址为:

  • 移位法:将各部分的最后一位对齐相加;
    使用移位叠加 \(5864 +4220+04 =1 0088\),故\(H(0-442-20586-4)= 0088\)(将分割后的每一部分的最低位对齐)。
  • 间界叠加法:从一端向另一端沿分割界来回折叠后,最后一位对齐相加。
    使用边界叠加法叠加 \(5864 +0224+04 =6092\),故\(H(0-442-20586-4)= 6092\)(从一端向另一端沿分割界来回叠加)。

6. 随机数法

取关键字的随机函数值为它的哈希地址,即 \(H(key) = random (key)\),
其中 \(random\) 为伪随机函数。
适用于:关键字长度不等

哈希冲突与处理方法

哈希冲突:元素关键字不同,但具有相同哈希地址,键值不再一一对应。
一般来说,哈希冲突很难避免。为了解决这个问题,需要找到新的尚未被占用的地址来存储该元素。

拉链法

将所有的冲突元素用单链表连接起来,每个链表对应一个单元;
此时,每个单元存储的不再是元素本身,而是相应单链表的头指针
思路类似于:邻接表

基于取模法的哈希函数:(x % N + N) % N
通常情况下,我们希望哈希函数的输出值处于 \([0,N-1]\) 的范围内,其中 \(N\) 是哈希表的大小。
而转换前的键值范围例如 \([10^{-9},10^9]\),可能存在负数。
为了解决这个问题,可以利用取模运算的性质:如果 \(a\) 和 \(b\) 都是整数,且 \(a\) 除以 \(b\) 得到的商为 \(q\),余数为 \(r\),则有 \(a = q * b + r\)。根据这个性质,我们可以将负数的取模运算转化为一个正数的取模运算。

单链表的建立:AcWing 826. 单链表 - 蒟蒻爬行中
此处插入操作选用头插法,链表头指针即存储在哈希表中。

#include<bits/stdc++.h>
using namespace std;

const int N = 100003;  //从100000开始的第一个素数
int n, x;
int h[N], e[N], ne[N], idx;

/* 构建哈希函数:取模法 */
int hashF(int x) {
	return (x % N + N) % N;	//索引值
}

/* 拉链法的插入操作 */
void insert(int x) {
	int k = hashF(x);
	e[idx] = x;	//数据数组
	ne[idx] = h[k];	//指针数组
	h[k] = idx++;	//哈希数组
}

/* 拉链法的查找操作 */
bool find(int x) {
	int k = hashF(x);
	//先找到索引值,再从该单元链表头开始查找
	for (int i = h[k]; i != -1; i = ne[i])
		if (e[i] == x)
			return true;
	return false;
}

int main()
{
	cin >> n;
	memset(h, -1, sizeof h);  //空指针一般用-1表示
	while (n--) {
		char op;
		cin >> op >> x;
		if (op == 'I')	insert(x);
		else cout << (find(x) ? "Yes\n" : "No\n");
	}
}

开放寻址法


  1. 一文看懂哈希表并学会使用C++ STL 中的哈希表_哈希表end函数-CSDN博客 ↩︎

  2. 哈希函数的常用构造方法 - 楼兰胡杨 - 博客园 (cnblogs.com) ↩︎

  3. 一文快速入门哈希表-CSDN博客 ↩︎

标签:key,840,int,函数,列表,关键字,地址,哈希,Acwing
From: https://www.cnblogs.com/haruhomu/p/17884063.html

相关文章

  • Python将列表数据保存为excel
    一、需求背景工作需要将列表数据写入到excel中,方便运营同学查看,数据示例如下:data_0=[[['Name','Age','Gender'],['Jack',22,'Male'],['Tom',34,'Female']],[['id&#......
  • Vue学习计划-Vue2--Vue核心(五)条件、列表渲染、表单数据
    1.条件渲染v-ifv-if="表达式"v-else-if="表达式"v-else="表达式"适用于:切换频率较低的场景特点:不显示dom元素,直接被删除注意:v-if和v-else-if、v-else一起使用,但要求结构不能被打断v-if和template一起使用,v-show不可以v-showv-show="表达式"适用于:切换频......
  • HTML列表标签学习
    一、有序列表例子:<ol> <li>苹果</li> <li>梨子</li></ol>其中有序列表可以有不同的排序方式:<h4>大写字母列表:</h4><oltype="A"><li>Apples</li><li>Bananas</li><li>Lemons</li><li>......
  • AcWing 802. 区间和
    题面:假定有一个无限长的数轴,数轴上每个坐标上的数都是\(0\)。现在,我们首先进行\(n\)次操作,每次操作将某一位置\(x\)上的数加\(c\)。接下来,进行\(m\)次询问,每个询问包含两个整数\(l\)和\(r\),求出在区间\([l,r]\)之间的所有数的和。原题链接:802.区间和-AcW......
  • 59AcWing 840. 模拟散列表
    点击查看代码#include<iostream>#include<cstring>usingnamespacestd;constintN=200003,null=0x3f3f3f3f;inth[N];intfind(intx){intk=(x%N+N)%N;//索引while(h[k]!=null&&h[k]!=x){k++;if(k==N)k=0;//重新搜索......
  • python--元组、列表、集合、字典、函数简单总结与区分
    元组:用“()”,不可修改其中的元素,有索引,tuple可建立一个元组。列表:用“【】”,可修改其中元素,有索引,可用list函数创建。集合:用“{}”,且{}相当于set()相当于set(【】),无序,无索引,可修改其中元素。字典:用”{}“,无索引,可修改其中元素,成对出现(区别于集合)。    例如:mynumber={"a":1,"b"......
  • Js中 列表里字典排序问题
    我又要给这样的列表,我想把出现"key3"的字典放到列表的后边constlist=[{key1:'value1',key2:'value2'},{key1:'value3',key2:'value4'},{key3:'value5',key2:'value6'},{key4:'......
  • Python中级之列表字典推导式和三元运算符
    列表生成式列表生成式是一种在Python中用于创建列表的简洁和优雅的语法。它允许你使用一行代码生成一个新的列表,而不必使用传统的循环语句。以下是列表生成式的基本语法:[expressionforiteminiterableifcondition]expression:用于生成新列表中每个元素的表达式。ite......
  • Acwing 5367. 不合群数
    题面:如果一个正整数无法被 \([2,a]\) 范围内的任何整数整除,则称其为不合群数。请你计算并输出 \([2,b]\) 范围内的最大不合群数。提示:\(10\) 亿内的最大质数是 \(999999937\),且相邻质数之间的差值均不超过 \(300\)原题链接:5367.不合群数-AcWing根据给出的提示判......
  • 列表初始化
    文章参考:爱编程的大丙(subingwen.cn)C++中有多种数据类型,如:变量、数组、对象。这些数据类型各自的初始化方式,并没有统一的方法。为了简化初始化过程,C++11提出了一种统一的初始化方法,即列表初始化。1.统一的初始化1.1C++98在C++98中,有两种情况可以使用列表初始化:普通的......