【C++】以哈希为底层结构的unordered系列的关联式容器


在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到以二为底N的对数,即在最差的情况下,需要比较红黑树的高度次,所以在数的节点非常多的时候,查询效率也不理想。最好的查询是,进行很少的比较次数就能将元素找到,因此在C++11中,STL又提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方法基本类似,只是其底层结构不同,它们四个分别是unordered_map、unordered_set、unordered_multimap和unordered_multiset。map系列是映射,set系列是集合。

unordered_map

  • unordered_map是存储<key,value>键值对的关联式容器,其允许通过key快速的索引到与其对应的value。
  • 在unordered_map中,键值通常用来唯一的标识元素,而映射值是一个对象,其内容与此键关联,键和映射值的类型可能不同。
  • 与以红黑树作为底层结构的map最大的不同是,unordered_map在内部没有对<key,value>按照任何特定的顺序排序,为了能在常数范围内找到key对应的value,unordered_map将相同哈希值的键值放在对应的桶中。
  • unordered_map容器通过key访问单个元素比map快,但是它通常在遍历元素子集的范围迭代方面的效率较低。
  • unordered_map实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value。

unordered_map的空构造

int main()
{
	unordered_map<int, string> umap;
	//构造空的unordered_map
	cout << umap.empty() << endl;
	//判空
	cout << umap.size() << endl;
	//求umap中有效元素的个数
	return 0;
}

unordered_map的迭代器

int main()
{
	//给umap中放入数据
	pair<int, string> v[] = {{3,"studying"}, {1,"I"}, {2,"love"}};
	int n = sizeof(v) / sizeof(v[0]);
	unordered_map<int, string> umap;

	for (int i = 0; i < n; ++i)
		umap.insert(v[i]);

	//利用迭代器对umap的数据进行访问
	unordered_map<int, string>::iterator it = umap.begin();
	while (it != umap.end())
	{
		cout << it->first << " : " << it->second << endl;
		it++;
	}
	//unordered_map的元素访问
	cout << umap[3] << endl;
	return 0;
}

执行结果:

3 : studying
1 : I
2 : love
studying

观察执行结果:不难看出,相较于map结构,我们用unordered_map定义的umap打印出来的结果是无序的。

unordered_map的元素访问

nordered_map重载了[],我们可以通过[]对数据进行访问,调用的时候,返回与key对应的value,如果没有对应的key,就返回一个默认值。要注意的是,operator[]函数中实际调用的是插入操作,用参数key和V()构造一个默认值往底层的哈希桶中插入,如果key不在哈希桶中,插入成功,返回V(),插入失败,说明key已经在哈希桶中,将key对应的value返回。

我们不仅可以通过[]来访问元素,也可以通过它来往unordered_map中插入数据,代码如下:

int main()
{
	//给umap中放入数据
	pair<int, string> v[] = {{3,"studying"}, {1,"I"}, {2,"love"}};
	int n = sizeof(v) / sizeof(v[0]);
	unordered_map<int, string> umap;

	for (int i = 0; i < n; ++i)
		umap.insert(v[i]);

	//利用迭代器对umap的数据进行访问
	unordered_map<int, string>::iterator it = umap.begin();
	while (it != umap.end())
	{
		cout << it->first << " : " << it->second << endl;
		it++;
	}
	cout << "size=" << umap.size() << endl;

	umap[4] = "China";//利用[]放入数据
	umap[9] = "C++";
	it = umap.begin();
	while (it != umap.end())
	{
		cout << it->first << " : " << it->second << endl;
		it++;
	}
	cout << "size=" << umap.size() << endl;
	return 0;
}

unordered_map的查询

int main()
{
	//给umap中放入数据
	pair<int, string> v[] = { {3,"studying"}, {1,"I"}, {2,"love"} };
	int n = sizeof(v) / sizeof(v[0]);
	unordered_map<int, string> umap;

	for (int i = 0; i < n; ++i)
		umap.insert(v[i]);
	auto p = umap.find(3);//查找函数,返回指向key结点的指针
	cout << p->first << " : " << p->second << endl;

	size_t cou = umap.count(2);
	cout << cou << endl;
	cou = umap.count(9);
	cout << cou << endl;
}

执行结果:

3 : studying
1
0

unordered_map的修改操作

int main()
{
	pair<int, string> v[] = { {3,"studying"}, {1,"I"}, {2,"love"} };
	int n = sizeof(v) / sizeof(v[0]);
	unordered_map<int, string> umap;
	umap.insert(v, v + n);//插入键值对,按区间插入
	umap.erase(3);//删除容器中的键值对
	umap.clear();//清空容器中所有的有效元素
	return 0;
}

unordered_map的桶操作

函数声明功能介绍
size_t bucket_count()const返回哈希桶中桶的总个数
size_t bucket_size(size_t n)const返回n号桶中有效元素的个数
size_t bucket(const K& key)返回元素key所在的桶号
int main()
{
	pair<int, string> v[] = { {3,"studying"}, {1,"I"}, {2,"love"} };
	int n = sizeof(v) / sizeof(v[0]);
	unordered_map<int, string> umap;
	umap.insert(v, v + n);

	cout << "bucket count = " << umap.bucket_count() << endl;
	for (int i = 0; i < umap.bucket_count(); ++i)
	{
		cout << i << " : " << umap.bucket_size(i) << endl;
	}

	int key[] = { 1,2,3 };
	for (int i = 0; i < n; ++i)
	{
		cout << key[i] << " : " << umap.bucket(key[i]) << endl;
	}
	return 0;
}

unordered_multimap

相对于unordered_map而言,unorderd_multimap的不同之处在于它允许重复元素的存在。

int main()
{
	pair<int, string> v[] = { {3,"studying"}, {1,"I"}, {2,"love"} ,{3,"learning"} };
	int n = sizeof(v) / sizeof(v[0]);
	unordered_multimap<int, string> ummap;
	ummap.insert(v, v + n);
	for (const auto &e : ummap)
	{
		cout << e.first << " : " << e.second << endl;
	}
	return 0; 
}

map和unordered_map的使用场景

如果我们要用到映射,map和unordered_map要怎样选择呢?我们知道这两者的不同就在于其底层实现结构的不同,前者底层结构是红黑树,后者底层结构是哈希表,对红黑树来说,其中序遍历是有序的,所以map的优势在于其底层结构的有序,所以如果我们对数据的顺序有要求的话,就采用map,其他的情况尽量采用unordered_map,因为它的底层结构是哈希表,数据的查询效率更高。另外就是如果对数据迭代访问的需求比较多的话,尽量采用map,如果查找的需求更多,访问的需求较少,那unordered_map就更加有优势。总的来说,排序和数据访问方面,map更有优势;数据查询方面,unordered_map更有优势,在实际应用中,它俩的选择可以根据实际需求来决定。

unordered_set

  • unordered_set是以不按特定顺序存储唯一元素的容器,并允许根据其值快速检索单个元素。
  • 在unordered_set中,元素的值同时也是其键,唯一标识它,键是不可变的,因此unordered_set中的元素不能在容器中修改,但是可以插入和删除。
  • 在内部,unordered_set不按任何特定顺序进行排序,而是根据其哈希值组织到桶中,以允许直接按其值快速访问单个元素。
  • unordered_set比有序的set更块的通过其键访问单个元素,但是在遍历元素子集的范围迭代方面效率较低。
int main()
{
	int ar[] = { 1,5,8,3,9,6,45,43,76,9,2,87 };
	int n = sizeof(ar) / sizeof(ar[0]);
	unordered_set<int> uset;

	uset.insert(ar, ar + n);

	for (const auto &e : uset)
		cout << e << " ";
	cout << endl;
	return 0;
}

执行结果:

9 1 45 5 8 43 3 6 76 2 87

unordered_set的用法和unordered_map非常接近,后面的用法可以参考unordere_set。

unordered_multiset

相对于unordered_set而言,unorderd_multiset的不同之处在于它允许重复元素的存在。

int main()
{
	int ar[] = { 1,5,8,3,9,6,45,43,76,9,2,87,87,87,87,87,87 };
	int n = sizeof(ar) / sizeof(ar[0]);
	unordered_multiset<int> uset;

	uset.insert(ar, ar + n);

	for (const auto &e : uset)
		cout << e << " ";
	cout << endl;
	return 0;
}

执行结果:

9 9 1 45 5 8 43 3 6 76 2 87 87 87 87 87 87
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值