【C++ 学习 ㉑】- 详解 map 和 set(上)

目录

一、C++ STL 关联式容器

二、pair 类模板

三、set

3.1 - set 的基本介绍

3.2 - set 的成员函数

3.1.1 - 构造函数

3.1.2 - 迭代器

3.1.3 - 修改操作

3.1.4 - 其他操作

四、map

4.1 - map 的基本介绍

4.2 - map 的成员函数

4.2.1 - 迭代器

4.2.2 - operator[]

五、multiset

六、multimap

七、相关练习

7.1 - 有效的括号

7.2 - 复制带随机指针的链表

7.3 - 两个数组的交集

7.4 - 前K个高频单词


 


一、C++ STL 关联式容器

C++ 容器可以分为序列式容器和关联式容器,我们在前面的章节中对序列式容器(包括 array、vector、list、deque 和 forward_list)已经做了详细的介绍,下面,我们也将对 C++ STL 中的所有关联式容器做详细的介绍。

和序列式容器不同的是,使用关联式容器存储的元素都是一个一个的 "键值对"(<key, value>),并且使用关联式容器存储的元素默认会根据各元素的键值 key 的大小做升序排序

由于不同的应用场景,C++ STL 实现了树形结构哈希结构的关联式容器。树形结构的关联式容器(包括 map、set、multimap、multiset)使用了平衡搜索树(即红黑树)作为其底层结构;哈希结构的关联式容器(包括 unordered_map、unordered_set、unordered_multimap、unordered_multise)则使用了哈希表作为其底层结构


二、pair 类模板

我们知道,关联式容器存储的是 "键值对" 形式的数据,因为 "键值对" 并不是普通数据类型,所以 C++ STL 提供了 pair 类模板,并将其定义在 <utility> 头文件中。

template < class T1, class T2 > struct pair; 

pair 类模板在 SGI-STL 版本中的实现

#pragma once
​
template<class T1, class T2>
struct pair
{typedef T1 first_type;typedef T2 second_type;
​T1 first;T2 second;pair() : first(T1()), second(T2()) {}pair(const T1& a, const T2& b) : first(a), second(b) {}
};
​
template<class T1, class T2>
inline bool operator==(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{return lhs.first == rhs.first && lhs.second == rhs.second;
}
​
template<class T1, class T2>
inline bool operator!=(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{return !(lhs == rhs);
}
​
template<class T1, class T2>
inline bool operator<(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{return lhs.first < rhs.first ||(!(rhs.first < lhs.first) && lhs.second < rhs.second);
}
​
template<class T1, class T2>
inline bool operator<=(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{return !(rhs < lhs);
}
​
template<class T1, class T2>
inline bool operator>(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{return rhs < lhs;
}
​
template<class T1, class T2>
inline bool operator>=(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{return !(lhs < rhs);
}
​
template<class T1, class T2>
inline pair<T1, T2> make_pair(const T1& x, const T2& y)
{return pair<T1, T2>(x, y);
}


三、set

3.1 - set 的基本介绍

set 容器以类模板的形式定义在 <set> 头文件中,并位于 std 命名空间中。

template < class T,                        // set::key_type/value_typeclass Compare = less<T>,        // set::key_compare/value_compareclass Alloc = allocator<T>      // set::allocator_type> class set;

set 是按照特定次序存储唯一元素的容器。

在 set 中,元素的 value 也标识它(value 就是 key,类型为 T),并且每个 value 必须是唯一的。set 中的元素不能在容器中修改(元素始终是 const),但可以在容器中插入或删除它们。

在内部,set 中的元素始终按照其内部比较对象(类型为 Compare)指定的特定严格弱序标准(strict weak ordering criterion)进行排序。

set 容器通过 key 访问单个元素的速度通常比 unorder_sort 容器慢,但是 set 容器允许根据子集的顺序直接迭代子集。

set 底层是用二叉搜索树(红黑树)实现的。

3.2 - set 的成员函数

3.1.1 - 构造函数

empty (1) explicit set(const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());
range (2) template<class InputIterator>set <InputIterator first, InputIterator last,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type()>;copy (3) set(const set& x);
  1. 默认构造函数:构造一个没有元素的空容器。

  2. 范围构造函数:用 [first, last) 区间中的元素构造一个容器。

  3. 拷贝构造函数。

3.1.2 - 迭代器

示例

#include <set>
#include <iostream>
using namespace std;
​
int main()
{int arr[] = { 75, 23, 65, 42, 13 };int sz = sizeof(arr) / sizeof(arr[0]);set<int> s(arr, arr + sz);
​set<int>::iterator it = s.begin();while (it != s.end()){// *it = 0;  // errorcout << *it << " ";++it;}// 13 23 42 65 75cout << endl;
​set<int>::reverse_iterator rit = s.rbegin();while (rit != s.rend()){// *rit = 0;  // errorcout << *rit << " ";++rit;}// 75 65 42 23 13cout << endl;return 0;
}

注意:set 容器的迭代器类型为双向迭代器,并且 iterator 等价于 const_iterator,reverse_iterator 等价于 const_reverse_iterator

这样就避免修改 set 容器中的元素

3.1.3 - 修改操作

insert:

single element (1) pair<iterator, bool> insert(const value_type& val);with hint (2) iterator insert(iterator position, const value_type& val);range (3) template<class InputIterator>void insert(InputIterator first, InputIterator last);

注意:在 set 中插入 val,实际上插入的是 <val, val> 构成的键值对

因为 set 中的元素都是唯一的,所以在插入操作中会检查每个插入的元素是否重复,如果是,则不插入该元素

而允许插入重复元素的类似容器是 multiset

返回值

  1. 如果插入成功,即插入的元素不重复,返回 pair<指向新插入的元素的迭代器, true>;如果插入失败,即插入的元素重复,返回 pair<指向 set 中已有的等效元素的迭代器, false>

  2. 返回一个迭代器,它指向新插入的元素,或者指向 set 中已有的等效的元素

erase:

(1)      void erase(iterator position);
(2) size_type erase(const value_type& val);
(3)      void erase(iterator first, iterator last);

swap:

void swap(set& x);

clear:

void clear();

示例

#include <set>
#include <iostream>
using namespace std;
​
int main()
{set<int> s;s.insert(75);s.insert(23);s.insert(65);s.insert(42);cout << s.insert(13).second << endl;  // 1(说明插入成功)cout << s.insert(13).second << endl;  // 0(说明插入失败)cout << s.size() << endl;  // 5for (const auto& e : s){cout << e << " ";}// 13 23 42 65 75cout << endl;return 0;
}

3.1.4 - 其他操作

find:

iterator find(const value_type& val) const;

count:

size_type count(const value_type& val) const;

返回 set 中值为 val 的元素的个数

因为 set 中的元素都是唯一的,所以返回值只能是 1(元素找到了) 或 0(元素没找到)

lower_bound:

iterator lower_bound(const value_type& val) const;

upper_bound:

iterator upper_bound(const value_type& val) const;

equal_range:

pair<iterator, iterator> equal_range(const value_type& val) const;

因为 set 中的元素都是唯一的,所以返回的范围中最多包含一个元素

示例

#include <set>
#include <iostream>
using namespace std;
​
int main()
{set<int> s;for (int i = 1; i < 10; ++i){s.insert(i * 10);}
​set<int>::iterator pos = s.find(10);if (pos != s.end())s.erase(pos);
​if (s.count(20))  // 等价于 s.find(20) != s.end()cout << "元素找到了" << endl;elsecout << "元素不存在" << endl;// 元素找到了
​// lower_bound 返回指向第一个 >= val 的元素的迭代器set<int>::iterator itlow = s.lower_bound(30); // upper_bound 返回指向第一个 > val 的元素的迭代器set<int>::iterator itup = s.upper_bound(60);cout << *itlow << endl;  // 40cout << *itup << endl;  // 70s.erase(itlow, itup);for (const auto& e : s){cout << e << " ";}// 20 70 80 90cout << endl;
​cout << *s.equal_range(20).first << endl;  // 20cout << *s.equal_range(20).second << endl;  // 70return 0;
}


四、map

4.1 - map 的基本介绍

map 容器以类模板的形式定义在 <map> 头文件中,并位于 std 命名空间中。

template < class Key,                                     // map::key_typeclass T,                                       // map::mapped_typeclass Compare = less<Key>,                     // map::key_compareclass Alloc = allocator<pair<const Key,T> >    // map::allocator_type> class map;

map 是关联式容器,它按照特定次序存储由键值 key 和值 value 组合而成的元素。

在 map 中,key 用于排序和唯一地标识元素,而 value 中存储与此 key 关联的内容。key 和 value 的类型可能不同,它们通过成员类型 value_type 绑定在一起:

typedef pair<const Key, T> value_type;

在内部,map 中的元素始终按照其内部比较对象(类型为 Compare)指定的严格弱排序标准(strict weak ordering criterion)通过 key 进行排序。

map 容器通过 key 访问单个元素的速度通常比 unordered_map 容器慢,但它允许根据子集的顺序直接迭代子集。

使用 [] 可以直接通过 key 找到对应的 value

map 底层是用二叉树搜索树(红黑树)实现的。

4.2 - map 的成员函数

4.2.1 - 迭代器

示例

#include <map>
#include <iostream>
using namespace std;
​
int main()
{map<string, string> dict;pair<string, string> kv("insert", "插入");dict.insert(kv);dict.insert(pair<string, string>("erase", "删除"));dict.insert(make_pair("find", "查找"));dict.insert({ "map", "地图;映射" });  // 多参数的构造函数的隐式类型转换
​auto it = dict.begin();while (it != dict.end()){// (*it).first = "xxx";  // error// (*it).second = "yyy";  // ok// cout << (*it).first << " : " << (*it).second << endl;// 或者:cout << it->first << " : " << it->second << endl;// 为了可读性,编译器将 it->->first/second 优化成了 it->first/second++it;}cout << endl;
​auto rit = dict.rbegin();while (rit != dict.rend()){cout << rit->first << " : " << rit->second << endl;++rit;}return 0;
}

map 容器的迭代器类型为双向迭代器

4.2.2 - operator[]

mapped_type& operator[](const key_type& k);
  1. 如果 k 与容器中的某个元素的 key 匹配,函数则返回该元素的 key 对应的 value 的引用

  2. 如果 k 与容器中所有元素的 key 都不匹配,函数则将插入键值为 k 的新元素,并返回其对应的 value 的引用(使用默认构造函数构造出来的)

mapped_type& operator[](const key_type& k)
{std::pair<iterator, bool> x = insert(make_pair(k, mapped_type()));return x.first->second;
}

示例

#include <map>
#include <iostream>
using namespace std;
​
int main()
{string fruits[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };map<string, int> countMap;for (const auto& e : fruits){++countMap[e];}for (const auto& e : countMap){cout << e.first << " : " << e.second << endl;}// 苹果 : 6// 西瓜: 3// 香蕉 : 2return 0;
}


五、multiset

multiset 容器以类模板的形式定义在 <set> 头文件中,并位于 std 命名空间中。

template < class T,                        // multiset::key_type/value_typeclass Compare = less<T>,        // multiset::key_compare/value_compareclass Alloc = allocator<T> >    // multiset::allocator_type> class multiset;

和 set 容器不同的是,multiset 容器中的元素可以重复

示例

#include <set>
#include <iostream>
using namespace std;
​
int main()
{multiset<int> s;s.insert(5);s.insert(3);s.insert(8);s.insert(7);s.insert(7);s.insert(9);s.insert(7);for (const auto& e : s){cout << e << " ";}// 3 5 7 7 7 8 9cout << endl;
​cout << s.count(7) << endl;  // 3
​auto ret = s.equal_range(7);auto itlow = ret.first;auto itup = ret.second;cout << *itlow << endl;  // 7cout << *itup << endl;  // 8s.erase(itlow, itup);for (const auto& e : s){cout << e << " ";}// 3 5 8 9cout << endl;return 0;
}


六、multimap

multimap 容器以类模板的形式定义在 <map> 头文件中,并位于 std 命名空间中。

template < class Key,                                     // multimap::key_typeclass T,                                       // multimap::mapped_typeclass Compare = less<Key>,                     // multimap::key_compareclass Alloc = allocator<pair<const Key,T> >    // multimap::allocator_type> class multimap;

和 map 容器不同的是,mutimap 容器中的元素的 key 可以是重复的,因此 multimap 没有重载 operator[]

示例

#include <map>
#include <iostream>
using namespace std;
​
int main()
{multimap<string, int> info;info.insert(make_pair("张三", 18));info.insert(make_pair("李四", 20));info.insert(make_pair("王五", 19));info.insert(make_pair("张三", 30));for (const auto& e : info){cout << e.first << " : " << e.second << endl;}// 李四 : 20// 王五: 19// 张三 : 18// 张三 : 30return 0;
}


七、相关练习

7.1 - 有效的括号

class Solution {
public:bool isValid(string s) {stack<char> st;map<char, char> matchMap;matchMap['('] = ')';matchMap['['] = ']';matchMap['{'] = '}';
​for (const auto& ch : s){if (matchMap.count(ch))  {st.push(ch);  // 左括号入栈}else{if (st.empty())return false;if (ch != matchMap[st.top()])  // 右括号去匹配栈顶的左括号return false;elsest.pop();}}return st.empty();}
};

7.2 - 复制带随机指针的链表

class Solution {
public:Node* copyRandomList(Node* head) {Node* cur = head;Node* copyHead = nullptr;Node* copyTail = copyHead;map<Node*, Node*> curCopyMap;while (cur){Node* copy = new Node(cur->val);curCopyMap[cur] = copy;
​if (copyHead == nullptr){copyHead = copyTail = copy;}else{copyTail->next = copy;copyTail = copyTail->next;}cur = cur->next;}
​cur = head;copyTail = copyHead;while (cur){if (cur->random == nullptr){copyTail->random = nullptr;}else{copyTail->random = curCopyMap[cur->random];}cur = cur->next;copyTail = copyTail->next;}return copyHead;}
};

7.3 - 两个数组的交集

class Solution {
public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {// 去重set<int> s1(nums1.begin(), nums1.end());set<int> s2(nums2.begin(), nums2.end());// 求交集vector<int> ret;set<int>::iterator it1 = s1.begin();set<int>::iterator it2 = s2.begin();while (it1 != s1.end() && it2 != s2.end()){if (*it1 < *it2){++it1;}else if (*it2 < *it1){++it2;}else{ret.push_back(*it1);++it1;++it2;}}return ret;}
};

拓展:如何求 nums1nums2 的差集

首先仍然是去重,然后和求交集不同的是,当按序比较两个数组中的元素时,值较小的元素属于差集,相等的两个元素则跳过

7.4 - 前K个高频单词

class Solution {
public:struct Greater {bool operator()(const pair<string, int>& lhs, const pair<string, int>& rhs){return lhs.second > rhs.second;}};
​vector<string> topKFrequent(vector<string>& words, int k) {map<string, int> countMap;for (const auto& e : words){++countMap[e];}
​vector<pair<string, int>> v(countMap.begin(), countMap.end());  // 注意:// 因为 v 已经按字典顺序排好序了,// 所以此时只需要按单词出现的频率由高到低进行(稳定)排序即可。stable_sort(v.begin(), v.end(), Greater());
​vector<string> ret;for (int i = 0; i < k; ++i){ret.push_back(v[i].first);} return ret;}
};

因为 map 的迭代器类型为双向迭代器,无法使用 sort,所以只能借助 vector 进行排序

又因为当不同的单词有相同的出现频率时,按字典顺序排序,所以必须使用稳定排序,即 stable_sort

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/133758.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决出现的java: 无法访问org.springframework.boot.SpringApplication问题~

错误描述如下所示&#xff1a; 错误原因&#xff1a;版本号匹配不一致导致的&#xff0c;61.0对应jdk17&#xff0c;52.0对应jdk8。 而我本地的java为java8&#xff0c;因此需要降低版本&#xff0c;即可解决该问题 <groupId>org.springframework.boot</groupId>…

EndNote21 | 安装及库的创建

EndNote21 | 安装及库的创建 一、EndNote21安装二、EndNote21库的创建 一、EndNote21安装 软件安装界面&#xff0c;双击“EndNote 21.exe”程序&#xff1b; 图1 安装软件界面点击next&#xff0c;选择30天试用&#xff0c;点击next&#xff1b; 图2 安装过程点击next&…

深入理解Linux网络笔记(一):内核是如何接收网络包的

本文为《深入理解Linux网络》学习笔记&#xff0c;使用的Linux源码版本是3.10&#xff0c;网卡驱动是Intel的igb网卡驱动 Linux源码在线阅读&#xff1a;https://elixir.bootlin.com/linux/v3.10/source 1、内核是如何接收网络包的 1&#xff09;、Linux网络收包总览 在TCP/I…

腾讯mini项目-【指标监控服务重构】2023-08-20

今日已办 PPT制作 答辩流程 概述&#xff1a;对项目背景、架构进行介绍&#xff08;体现我们分组的区别和需求&#xff09;人员&#xff1a;小组成员进行简短的自我介绍和在项目中的定位&#xff0c;分工进展&#xff1a;对项目进展介绍&#xff0c;其中a、b两组的区别和工作…

Azure + React + ASP.NET Core 项目笔记一:项目环境搭建(二)

有意义的标题 pnpm 安装umi4 脚手架搭建打包语句变更Visual Studio调试Azure 设置变更发布 pnpm 安装 参考官网&#xff0c;或者直接使用npm安装 npm install -g pnpmumi4 脚手架搭建 我这里用的umi4&#xff0c;官网已附上 这里需要把clientapp清空&#xff0c;之后 cd Cl…

YUVToRGB(CUDA Conversion)库的学习

目录 前言1. YUVToRGB1.1 Supported Feature1.2 Performance1.2.1 Performance Table1.2.2 How to Benchmark1.2.3 How to Verify the Accuracy 1.3 User Integration1.4 Summary 2. YUVToRGB案例2.1 环境配置2.2 run案例 3. YUVToRGB浅析4. 补充知识4.1 YUV不同格式区别4.2 Lu…

代码随想录算法训练营Day60 | 84. 柱状图中最大的矩形

文章目录 84. 柱状图中最大的矩形首尾加 0双指针 84. 柱状图中最大的矩形 题目链接 | 解题思路 本题和接雨水的题目相互呼应&#xff0c;但是难度略有提升&#xff0c;同样是一道非常棒的题&#xff01; 在接雨水中&#xff0c;需要找到每一列的左侧最大值和右侧最大值&…

Mybatis基础知识(一)

Mybatis基础知识(一) Mybatis基础知识 Mybatis基础知识(一)一、MyBatis特性二、和其它持久化层技术对比三、MyBatis的简单使用1、创建maven工程2、创建pojo对象3、创建MyBatis的核心配置文件①properties②typeAliases③environments:④mappers:引入映射文件 4、创建mapper接口…

fabic如何将绘图原点移到画布中心

情况说明&#xff1a; fabic默认绘图原点为left&#xff1a;0&#xff0c;top&#xff1a;0 后端给我的内容是按照x&#xff0c;y返回的&#xff0c;需要将坐标系移到fabic画布的中心位置&#xff0c;找了下网上合适的砖&#xff0c;想一句命令直接设置&#xff0c;结果没有。…

C++QT day7

仿照vector手动实现自己的myVector&#xff0c;最主要实现二倍扩容功能 #include <iostream>using namespace std;template<typename T> class my_vector {int size;//可存储的容量大小int num;//当前存储的元素个数T* data;//存储数据的空间地址public://无参构造…

zabbix 钉钉微信企微告警(动作操作消息内容模板)

一、环境配置 1、配置zabbix服务端 2、配置监控主机&监控项&监控模板 zabbix配置安装_this page is used to test the proper operation of _疯飙的蜗牛的博客-CSDN博客 二、触发器 触发器的本质就是一个条件判断&#xff0c;对于不同的监控数据来说&#xff0c;我…

uniapp H5生成画布,插入网络图片。下载画布

因为网络图片不能直接使用ctx.drawImage(&#xff09;插入。得使用uni.getImageInfo()方法下载后插入。 但是当画布中存在多张网络图片时&#xff0c;必须等待uni.getImageInfo()下载完成后才行。这样得下载套下载。太过于繁琐。所以定义了一个递归下载方法。同时避免下载图片异…

k8s集群使用ingress转发grafana服务

文章目录 前言一、思路二、grafana准备1. grafana-configmap.yaml2. grafana.yaml 三、ingress准备1. ingress.yaml2. grafana-externalname.yaml3. ingress-nginx-controller 四、 本机host文件准备五、访问测试 前言 在k8s集群中&#xff0c;使用ingress服务转发grafana的页…

centos安装flink,通过windows访问webui

1. 安装flink 1.1. flink的下载 通过flink官网下载flink安装包 https://flink.apache.org/ 下载安装包 1.2 flink在centos上的安装 将下载好的flink-1.17.1-bin-scala_2.12.tgz安装包放到centos目录下 解压文件&#xff1a; [rootlocalhost ~]# tar -zxvf flink-1.17.…

利用Windows搭建Emby媒体库服务器,轻松实现无公网IP的远程访问

文章目录 1.前言2. Emby网站搭建2.1. Emby下载和安装2.2 Emby网页测试 3. 本地网页发布3.1 注册并安装cpolar内网穿透3.2 Cpolar云端设置3.3 Cpolar内网穿透本地设置 4.公网访问测试5.结语 1.前言 在现代五花八门的网络应用场景中&#xff0c;观看视频绝对是主力应用场景之一&…

基于SpringBoot + Vue的项目整合WebSocket的入门教程

1、WebSocket简介 WebSocket是一种网络通信协议&#xff0c;可以在单个TCP连接上进行全双工通信。它于2011年被IETF定为标准RFC 6455&#xff0c;并由RFC7936进行补充规范。在WebSocket API中&#xff0c;浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性…

Python 基础知识结构

一、关键字 二、内置函数 三、运算符 1、算数运算符 加 数字与字符串拼接 - 减 * 乘 数字与字符串重复 / 除 返回浮点数 % 取模 返回除法的余数 奇偶判断 ** 幂次 // 整除 返回商的整数部分&#xff0c;向下取整数&#xff0c;注意&#xff1a;-10//3,出现负数时的情况只要参…

北斗+5G 织就精确定位的“天罗地网”

北斗卫星导航系统&#xff08;简称北斗系统&#xff09;是我国自主发展、独立运行的全球卫星导航系统&#xff0c;为全球用户提供全天候、全天时、高精度的定位、导航和授时服务。2020年7月31日&#xff0c;北斗三号系统建成开通并提供全球服务&#xff0c;北斗系统进入全面推广…

基于element-ui的年份范围选择器

基于element-ui的年份范围选择器 element-ui官方只有日期范围和月份范围选择器&#xff0c;根据需求场景需要&#xff0c;支持年份选择器&#xff0c;原本使用两个分开的年份选择器实现的&#xff0c;但是往往有些是不能接受的。在网上找了很多都没有合适的&#xff0c;所以打…

第3章_瑞萨MCU零基础入门系列教程之开发环境搭建与体验

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…