首页 > 编程语言 >C++ 中的 map, unordered_map, cc_hash_table, gp_hash_table 简记

C++ 中的 map, unordered_map, cc_hash_table, gp_hash_table 简记

时间:2023-08-16 11:02:16浏览次数:39  
标签:map hash gp cc int table

做题时,常常会用到查重操作,可以使用 STL 中的 map 与 unordered_map ,也可以使用 “平板电视” 中的 cc_hash_table 和 gp_hash_table 实现。

\(\texttt{map}\)

map 的内部实现是红黑树,插入、查找元素的时间复杂度都是 \(O(\log n)\)。

map<int,bool>m;
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
	int t;
	cin>>t;
	m[t]=1;
}
cin>>n;
for(int i=1;i<=n;i++)
{
	int t;
	cin>>t;
	if(m.count(t))
		cout<<"have\n";
	else cout<<"nohave\n";
}

Input

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

Output

have
have
nohave
have
have

count(x) 返回的是以 x 为键值的元素个数。显然这个值只可能为 1 或者 0.我们用它检验元素是否存在。用 find(x)!=end() 也可以达到同样效果。

重点来了:map 易错点

如果将上述代码的 if(m.count(t)) 换成 if(m[t]==1),你会发现输出的答案仍然是正确的,但是,如果在查询量大的题目中,后者的时间复杂度会异常地大,有时可能导致 TLE。这种情况非常坑,有时是一直 TLE 而调不出来代码导致心态崩溃的罪魁祸首。

就比如这两份记录:

https://atcoder.jp/contests/abc265/submissions/34250287

https://atcoder.jp/contests/abc265/submissions/34250846

什么原因呢?让我们每次查询后输出 map 的每一个元素试试看

map<int,bool>m;
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
	int t;
	cin>>t;
	m[t]=1;
}
cin>>n;
for(int i=1;i<=n;i++)
{
	int t;
	cin>>t;
	if(m.count(t))cout<<"have\n";
	else cout<<"nohave\n";
	cout<<"Case "<<i<<":\n";
	for(auto i:m)
	cout<<i.first<<' '<<i.second<<'\n';
	cout<<'\n';
}

Input

3
1 2 3
3
1
4
5

Output

have
Case 1:
1 1
2 1
3 1

nohave
Case 2:
1 1
2 1
3 1

nohave
Case 3:
1 1
2 1
3 1

使用 count() 的情况下,一切正常,符合预期。

把上面的 m.count(t) 改成 m[t]==1

Input

同上。

Output

have
Case 1:
1 1
2 1
3 1

nohave
Case 2:
1 1
2 1
3 1
4 0

nohave
Case 3:
1 1
2 1
3 1
4 0
5 0

竟然莫名多了 \(val=0\) 的元素!

是的,当 map 使用 [] 访问不存在的元素时,它不会报错,而会插入一个新的 \(key=访问的key,val=0\) 的元素。这样,就导致我们查询的复杂度一下子从 \(O\big(m\log n\big)\) 变成 \(O\big(m\log (n+m)\big)\)。

有的题目只需要访问元素而本身不需要判断某个元素是否存在,这时我们一定不要忘了要加上判断其是否存在的这句话。对于 \(val=0\) 元素本身有意义的题目,没判存在的情况用 [] 还会导致 WA。

map 的遍历

map 的每个元素被表示成一个 pair,其中 first 为 key,second 为 value。

他的遍历可以这么写:

map<string,int>m;
for(pair<string,int>c:m)
{
	cout<<c.first<<' ';
	cout<<c.second<<'\n';
}

或者直接

map<string,int>m;
for(auto c:m)
{
	cout<<c.first<<' ';
	cout<<c.second<<'\n';
}

当然也可以用迭代器,但是太麻烦了。

\(\texttt{unordered_map}\)

unordered_map 和 map 使用方法和特点类似,只是由于无序,它的插入和查询都是均摊 \(O(1)\) 的,最坏情况由于刻意制造的哈希冲突,会变成 \(O(n)\)。

遍历与 map 同。

\(\texttt{pb_ds}\)

俗称平板电视,是扩展库,封装字典树、堆、平衡树、哈希等数据结构。这里主要讲 pd_ds 中的哈希表(即 cc_hash_table 和 gp_hash_table)。使用 pb_ds 的哈希表之前,我们要有这样的头文件和命名空间声明:

#include <ext/pb_ds/hash_policy.hpp>
using namespace __gnu_pbds;
/*
鉴于其中某些成员名称和标准命名空间的冲突,
有时避免错误可以不进行命名空间声明,
而在每次使用时用 __gnu_pbds::xxx; 的方式访问
*/

cc_hash_table 和 gp_hash_table 的主要区别为处理哈希冲突的方式。

gp_hash_table 使用查探法,cc_hash_table 使用拉链法。cc 常常跑的更快些。

和 unordered_map 类似,也是无序的。插入查询均摊 \(O(1)\)。

值得注意的是,这两个都没有 count() 函数,我们只能用 find(x)!=end() 做查询是否存在。

代码:

#include <ext/pb_ds/hash_policy.hpp>
using namespace __gnu_pbds;
/*etc*/

gp_hash_table <int,int> g;
cc_hash_table <int,int> c;
inline void solve(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		if(i&1) g[i]=1;
		else c[i]=1;
	}
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int t;
		cin>>t;
		
		if(g.find(t)!=g.end()) cout<<"have g\n";
		else cout<<"nohave g\n";

		if(c.find(t)!=c.end()) cout<<"have c\n";
		else cout<<"nohave c\n";
	}
}

他们也会在 [] 访问不存在的元素时,插入一个它,服了。

遍历与 map 同。做题时,常常会用到哈希,哈希的实现可以使用 STL 中的 map 与 unordered_map ,也可以使用 “平板电视” 中的 cc_hash_table 和 gp_hash_table。

标签:map,hash,gp,cc,int,table
From: https://www.cnblogs.com/xcrr/p/17633426.html

相关文章

  • Map简介
    一、Map特点1.Map集合概述:Map集合是双列集合,每个元素拥有两个数据Map集合每个元素的格式为:key=value(键值对集合),因此也可以称作键值对集合2.Map集合体系特点:Map集合的特点由键决定Map集合的键是无序,不重复,无索引的,值可重复,且可以为nullMap集合后面重复的键对应的值会覆盖前面......
  • 执行kubeadm 出现 FATAL: the ConfigMap "kubeadm-config" in the kube-system namesp
    现象: [upgrade/config]Makingsuretheconfigurationiscorrect:[upgrade/config]Readingconfigurationfromthecluster...[upgrade/config]FYI:Youcanlookatthisconfigfilewith'kubectl-nkube-systemgetcmkubeadm-config-oyaml'[upgrade/c......
  • 集合+hashmap
    数组数组(Array)是一种用连续的内存空间存储相同数据类型数据的线性数据结构。面试题:为什么数组索引从0开始?假如从1开始会怎么样?操作数组的时间复杂度当未知数组查询时,时间复杂度为O(n)总结———————————————————————————————————......
  • 【Python】解决“Tk_GetPixmap: Error from CreateDIBSection”闪退问题
    解决Python使用Tkinter的Notebook切换标签时出现的“Tk_GetPixmap:ErrorfromCreateDIBSection操作成功完成”闪退问题零、问题描述在使用Tkinter的Notebook控件时,对其标签进行切换,发现切换不了,一切换就报如下图错误:第一个页面正常显示,后面的就都不行了,都是报这个错误。第......
  • 【校招VIP】java语言考点之ConcurrentHashMap1.7和1.8
    考点介绍:ConcurrentHashMap是JAVA校招面试的热门考点,主要集中在1.7和1.8的底层结构和相关的性能提高。理解这个考点要从map本身的并发问题出发,再到hashTable的低性能并发安全,引申到ConcurrentHashMap的分块处理。同时要理解读锁和写锁的区别一、考点题目1、ConcurrentHashMap与......
  • Mybatis中的resultType和resultMap
    综述MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap,resultType是直接返回设置的类型,而resultMap则是对外部ResultMap的引用,但是resultType跟resultMap不能同时存在。在MyBatis进行查询映射时,其实查询出来的每一个属性都是放在一个对应的Map里面......
  • Vue进阶(幺伍贰):el-table-column :key 应用
    (文章目录)一、前言在前端项目开发过程中,el-table展示的结果列使用组件形式引入,其中某些字段通过:formatter方法转码,结果栏位的字段显示/隐藏控制也使用组件形式引入,前端在控制字段显示属性时,发现码值转换及字段信息展示均有问题。二、问题分析通过阅读代码结构,发现el-table-co......
  • HashMap遍历方式
    HashMap是一个键值对的集合,我们不能通过简单的循环来遍历HashMap,所以我们一般通过以下两种方式来遍历HashMap,一种是通过KeySet集合来遍历,另一种是通过entry键值对对象来遍历。KeySet遍历HashMap通过keySet()方法获取HashMap的keySet集合遍历keySet集合,可以使用iterator迭代器......
  • ADM4016I The index indexName on the source table source-table does not match any
    ADM4016I Theindex indexName onthesourcetable source-table doesnotmatchanypartitionedindexesonthetargettable target-table .ALTERTABLEATTACHprocessingcontinues.https://www.ibm.com/docs/en/db2/10.5?topic=messages-adm0000-adm5999LastUp......
  • Stable Diffusion学习笔记
    一、使用讯飞星火大模型生成StableDiffusionprompt(提示词)#StableDiffusionprompt助理你来充当一位有艺术气息的StableDiffusionprompt助理。##任务我用自然语言告诉你要生成的prompt的主题,你的任务是根据这个主题想象一幅完整的画面,然后转化成一份详细的、高......