数据结构进阶 unordered_set unordered_map的使用

简介: 数据结构进阶 unordered_set unordered_map的使用

unordered系列关联式容器


在C++98中 STL提供了底层为红黑树的一系列关联式容器 在查询时效率可以达到Log(N)


即在最差的情况下 查询红黑树的高度次 这个时候的效率也不太理想


最好的查询是 通过很少的比较次数就能够将被查询元素找到


因此 在C++11中 STL又提供了四个unordered系列的关联式容器


这四个容器与map和set的用法相似 但底层结构不同


unordered_set介绍

unordered_set是不按照特定顺序存储键值的关联式容器 其允许通过键值快速索引到对应元素

在unordered_set中 元素的值也是唯一标识它的key

在内部 unordered_set中的元素没有按照任何特定顺序排列 为了能在常数时间内找到key unordered_set将相同哈希值的键值放在桶中

unordered_set查找单个元素的效率比set快 但是它遍历元素子集的范围迭代效率差

它的迭代器至少是前向迭代器


unordered_set的使用


unordered_set的定义方式


方式一 构造一个某类型空容器


unordered_set<int> us1; // 构造一个int类型的空容器


方式二 拷贝构造某同类型容器的复制品


unordered_set<int> us2(us1); // 构造某类型容器的复制品


方式三 使用迭代器拷贝构造一段内容


string s1("hello world");
  unordered_set<char> us3(s1.begin(), s1.end()); // 使用string的迭代器拷贝构造


unordered_set的接口函数

48ed47b11ff54ce9b95378beb9e439af.png

迭代器相关函数如下


869135bc154946cda9b8adba2a4c678b.png

使用示例如下


展示去重和范围for遍历


unordered_set<int> us1; // 构造一个int类型的空容器
  us1.insert(3);
  us1.insert(3);
  us1.insert(5);
  us1.insert(1);
  us1.insert(7);
  us1.insert(8);
  for (auto x: us1)
  {
  cout << x << endl;
  }


我们使用unordered_set容器 并且插入多组重复数据 之后使用范围for遍历 达到一个去重的效果


注意 这里和set的区别是不会排序


展示直接去重


unordered_set<int> us1; // 构造一个int类型的空容器
  us1.insert(3);
  us1.insert(3);
  us1.insert(5);
  us1.insert(1);
  us1.insert(7);
  us1.insert(8);
  for (auto x : us1)
  {
  cout << x << " ";
  }
  cout << endl;
  us1.erase(3);
  for (auto x : us1)
  {
  cout << x << " ";
  }
  cout << endl;


我们调用erase删除我们想要删除的值 并且再遍历一遍容器


4b1ec5543eb04fffa9e34d4b2b2b146f.png

我们可以发现 3被删除了


那么假如我们连续两次删除3会发生什么呢?


5bff8332498d4e67af8fa1fbd6f2b67a.png


展示迭代器遍历

代码如下


unordered_set<int> us1; // 构造一个int类型的空容器
  us1.insert(3);
  us1.insert(3);
  us1.insert(5);
  us1.insert(1);
  us1.insert(7);
  us1.insert(8);
  unordered_set<int>::iterator it = us1.begin();
  while (it != us1.end())
  {
  cout << *it << " ";
  it++;
  }
  cout << endl;

展示效果如下


8129d2eae6ad49849aee764762fe60e4.png


展示查找计数交换等


unordered_set<int> s1; // 构造一个int类型的空容器
  s1.insert(1);
  s1.insert(3);
  s1.insert(5);
  s1.insert(3);
  s1.insert(4);
  s1.insert(9);
  s1.insert(7);
  s1.insert(4);
  cout << s1.count(3) << endl;
  cout << s1.count(2) << endl;
  cout << s1.size() << endl;
  s1.clear();
  cout << s1.size() << endl;
  cout << s1.empty() << endl;


我们可以发现查找 3 2 出现的次数


s1的大小 清空后的大小 以及s1是否为空


ad03ca06956140368856a7245efc5627.png


展示交换容器


unordered_set<int> s1; // 构造一个int类型的空容器
  s1.insert(1);
  s1.insert(3);
  s1.insert(5);
  s1.insert(3);
  s1.insert(4);
  s1.insert(9);
  s1.insert(7);
  s1.insert(4);
  unordered_set<int> s2;
  cout << s2.size() << endl;
  cout << s1.size() << endl;
  s2.swap(s1);
  cout << s2.size() << endl;
  cout << s1.size() << endl;

我们可以发现 交换后两个容器的大小发生了变化


unordered_multiset

unordered_multiset和unordered_set的底层实现都是相同的 接口函数也十分类似


它们之间最大的区别就是 unordered_multiset中允许键值对冗余


unordered_multiset<int> us1; // 构造一个int类型的空容器
  us1.insert(3);
  us1.insert(3);
  us1.insert(5);
  us1.insert(1);
  us1.insert(7);
  us1.insert(8);
  for (auto x : us1)
  {
  cout << x << " ";
  }


我们可以看到运行结果有冗余的键值

a4a7c3ec935d42a78df7bd17cd34ea48.png


由于multiset容器允许键值冗余,因此两个容器中成员函数find和count的意义也有所不同


成员函数find                                     功能

unordered_set容器                   返回键值为val的元素的迭代器

unordered_multiset容器    返回底层哈希表中第一个找到的键值为val的元素的迭代器

成员函数count                              功能

unordered_set容器           键值为val的元素存在则返回1,不存在则返回0(find成员函数可替代)

unordered_multiset容器     返回键值为val的元素个数(find成员函数不可替代)


unordered_map的介绍


1 unordered_map是储存<key,vlaue>键值对的关联式容器 其允许通过key值快速的索引到对应value值

2 在unordered_map中 键值通常用于唯一的标识元素 而映射值是一个对象 映射内容与此键关联 它们的类型可能不同

3 在内部 unordered_map没有对<key , value>按照任何顺序排序 为了能在常数范围内找到key值对应的value unordered_map将相同的哈希值的键值放在相同的桶中

4 unordered_map通过key值访问单个元素比map快 但是在遍历元素子集的迭代方面效率差

5 unordered_map实现了直接访问操作符【】 它允许使用key作为参数直接访问value

6 它的迭代器至少是前向迭代器


unordered_map的使用


unordered_map的定义方式


方式一 构造一个某类型的空容器


unordered_map<int, int> um1; // 构造一个某类型的空容器


方式二 拷贝构造某类型容器的复制品

unordered_map<int, int> um2(um1); // 拷贝构造某类型容器的复制品


方式三 使用迭代器拷贝构造某一段内容

unordered_map<int, int> um3(um1.begin(), um1.end());// 使用迭代器拷贝构造某一段内容


unordered的接口函数

710e947609574184ac0c18eca84fd14b.png

除了上述接口函数外 unordered_map还提供了一个功能十分强大的接口函数【】


若当前容器中已有键值为key的键值对 则返回该键值对于value的引用

若当前容器中无键值为key的键值对 则先插入键值对<key,value()> 之后返回该键值对于value的引用

迭代器相关函数如下

6f66abe4dc8945e0b9ee2e9ca5bbf2d8.png


使用示例如下


展示去重和范围for遍历


unordered_map<int, int> um1; // 构造一个某类型的空容器
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(5, 5));
  um1.insert(make_pair(1, 1));
  um1.insert(make_pair(7, 7));
  um1.insert(make_pair(8, 8));
  for (auto x : um1)
  {
  cout << x.first << " ";
  }
  cout << endl;


我们使用unordered_map容器 并且插入多组重复数据之后使用范围for遍历 达到一个去重的效果


注意 这里和map的区别是不会排序

8e0e292a65a7449eaab3479af1442adb.png



展示直接去重


unordered_map<int, int> um1; // 构造一个某类型的空容器
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(5, 5));
  um1.insert(make_pair(1, 1));
  um1.insert(make_pair(7, 7));
  um1.insert(make_pair(8, 8));
  for (auto x : um1)
  {
  cout << x.first << " ";
  }
  cout << endl;
  um1.erase(3);
  for (auto x : um1)
  {
  cout << x.first << " ";
  }
  cout << endl;

我们调用erase删除我们想要删除的key值 并且再遍历一遍容器

aa3b106085634cf59c34285e0988c57a.png


我们可以发现3被删除了


那么假如我们连续两次删除3会发生什么呢?

141a477c5fae4a7f92cf7a1bb07e621c.png



展示迭代器遍历

代码如下


unordered_map<int, int> um1; // 构造一个某类型的空容器
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(5, 5));
  um1.insert(make_pair(1, 1));
  um1.insert(make_pair(7, 7));
  um1.insert(make_pair(8, 8));
  auto it = um1.begin();
  while (it != um1.end())
  {
  cout << it->first << " ";
  it++;
  }
  cout << endl;


展示效果如下


c153a537e961422b8e8613b16e25882f.png


展示查找

unordered_map的查找是根据key值在unordered_map中查找 有两种结果


如果找到了则返回对应的迭代器

如果找不到则返回end迭代器

代码和演示效果如下


unordered_map<int, int> um1; // 构造一个某类型的空容器
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(5, 5));
  um1.insert(make_pair(1, 1));
  um1.insert(make_pair(7, 7));
  um1.insert(make_pair(8, 8));
  auto it = um1.find(3);
  auto it2 = um1.find(4);
  if (it != um1.end())
  {
  cout << it->first << endl;
  }
  if (it2 != um1.end())
  {
  cout << it2->first << endl;
  }

6fe0d3a59a1c4d7aa89d04c29437cf7f.png



我们可以发现 确实符合上面的规则


展示【】运算符

首先我们明确下它的规则


如果key不在unordered_map中 则使用【】会插入键值对 然后返回该键值对中value的引用

如果key在unordered_map中则会返回键值为K的元素的value的引用

测试代码和结果如下


unordered_map<int, int> um1; // 构造一个某类型的空容器
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(5, 5));
  um1.insert(make_pair(1, 1));
  um1.insert(make_pair(7, 7));
  um1.insert(make_pair(8, 8));
  um1[3] = 20;
  um1[9] = 9;
  auto it = um1.find(3);
  cout << it->second << endl;
  for (auto x : um1)
  {
  cout << x.first << " ";
  }

5b4bd602bdf84076b784c3aba7a5873f.png


我们可以看到 进行了


um1[3] = 20;
  um1[9] = 9;


这两步操作之后


key值为3的value变成了20


多了一个key值为9的元素


展示计数清除等

这里很简单 直接看代码和演示结果就可以


unordered_map<int, int> um1; // 构造一个某类型的空容器
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(5, 5));
  um1.insert(make_pair(1, 1));
  um1.insert(make_pair(7, 7));
  cout << um1.size() << endl;
  um1.clear();
  cout << um1.size() << endl;

7125580ce3e34257b68c032dad8bb00c.png


unordered_multimap

unordered_multimap和unordered_map的底层实现都是相同的 接口函数也十分类似


它们之间最大的区别就是 unordered_multimap中允许键值对冗余


代码和演示结果如下


unordered_multimap<int, int> um1; // 构造一个某类型的空容器
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(3, 3));
  um1.insert(make_pair(5, 5));
  um1.insert(make_pair(1, 1));
  um1.insert(make_pair(7, 7));
  for (auto x : um1)
  {
  cout << x.first << " ";
  }

9fbd91adc6594057a47bd7504433f1a4.png

由于unordered_multimap容器允许键值对的键值冗余 因此该容器中成员函数find和count的意义与unordered_map容器中的也有所不同


成员函数find                                   功能

unordered_map容器    返回键值为key的键值对的迭代器

unordered_multimap容器      返回底层哈希表中第一个找到的键值为key的键值对的迭代器

成员函数count                      功能

unordered_map容器   键值为key的键值对存在则返回1,不存在则返回0(find成员函数可替代)

unordered_multimap容器    返回键值为key的键值对的个数(find成员函数不可替代)


此外由于unordered_multimap容器允许键值对冗余 因此【】操作符可能会产生歧义 因此在unordered_multimap容器中并未实现之

相关文章
|
16天前
|
存储 Java
告别混乱!用Java Map优雅管理你的数据结构
【10月更文挑战第17天】在软件开发中,随着项目复杂度增加,数据结构的组织和管理至关重要。Java中的Map接口提供了一种优雅的解决方案,帮助我们高效、清晰地管理数据。本文通过在线购物平台的案例,展示了Map在商品管理、用户管理和订单管理中的具体应用,有效提升了代码质量和维护性。
59 2
|
16天前
|
存储 Java 开发者
Java Map实战:用HashMap和TreeMap轻松解决复杂数据结构问题!
【10月更文挑战第17天】本文深入探讨了Java中HashMap和TreeMap两种Map类型的特性和应用场景。HashMap基于哈希表实现,支持高效的数据操作且允许键值为null;TreeMap基于红黑树实现,支持自然排序或自定义排序,确保元素有序。文章通过具体示例展示了两者的实战应用,帮助开发者根据实际需求选择合适的数据结构,提高开发效率。
47 2
|
14天前
|
存储 Java 开发者
Java中的Map接口提供了一种优雅的方式来管理数据结构,使代码更加清晰、高效
【10月更文挑战第19天】在软件开发中,随着项目复杂度的增加,数据结构的组织和管理变得至关重要。Java中的Map接口提供了一种优雅的方式来管理数据结构,使代码更加清晰、高效。本文通过在线购物平台的案例,展示了Map在商品管理、用户管理和订单管理中的具体应用,帮助开发者告别混乱,提升代码质量。
24 1
|
26天前
|
存储 JavaScript 前端开发
Set、Map、WeakSet 和 WeakMap 的区别
在 JavaScript 中,Set 和 Map 用于存储唯一值和键值对,支持多种操作方法,如添加、删除和检查元素。WeakSet 和 WeakMap 则存储弱引用的对象,有助于防止内存泄漏,适合特定场景使用。
|
2月前
|
存储 Java API
【数据结构】map&set详解
本文详细介绍了Java集合框架中的Set系列和Map系列集合。Set系列包括HashSet(哈希表实现,无序且元素唯一)、LinkedHashSet(保持插入顺序的HashSet)、TreeSet(红黑树实现,自动排序)。Map系列为双列集合,键值一一对应,键不可重复,值可重复。文章还介绍了HashMap、LinkedHashMap、TreeMap的具体实现与应用场景,并提供了面试题示例,如随机链表复制、宝石与石头、前K个高频单词等问题的解决方案。
34 6
【数据结构】map&set详解
|
29天前
|
存储 缓存 Java
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
31 1
|
1月前
|
存储 安全
【数据结构】Set的使用与注意事项
【数据结构】Set的使用与注意事项
19 2
|
1月前
|
存储 自然语言处理 安全
【数据结构】Map的使用与注意事项
【数据结构】Map的使用与注意事项
24 1
|
2月前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
35 5
|
2月前
|
存储 JavaScript 前端开发
js的map和set |21
js的map和set |21