【C++:map和set的使用】C++STL容器详解:set容器从使用到高频算法题实战

【C++:map和set的使用】C++STL容器详解:set容器从使用到高频算法题实战

🔥艾莉丝努力练剑:个人主页

专栏传送门:《C语言》《数据结构与算法》C/C++干货分享&学习过程记录Linux操作系统编程详解笔试/面试常见算法:从基础到进阶测试开发要点全知道

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬艾莉丝的简介:


🎬艾莉丝的C++专栏简介:

​​​


目录

C++的两个参考文档

1  ~>  序列式容器和关联式容器

2  ~>  了解set容器

2.1  set类介绍

2.2  看set文档

2.2.1  set的构造和迭代器

2.2.2  拷贝构造

3  ~>​  set的使用层详解

3.1  增删查

3.1.1  增:insert插入

3.1.2  删:erase删除

3.1.3  查

3.1.4  单纯判断在不在:count

3.1.5  lower_bound和upper_bound

3.2  insert和迭代器遍历

3.2.1  代码实现

3.2.2  运行演示

3.3  find和erase

3.3.1  代码实现

3.3.2  运行演示

3.4  multiset

3.4.1  和set之间的差异

3.4.2  代码实现

3.4.3  查找多个key

3.4.4  equal_range

3.4.5  count函数

3.4.6  运行演示

3.5  交集和差集的作用:数据同步和数据备份

3.5.1  图示:数据同步和数据备份

3.5.2  博主手记

3.6  博主手记

4  ~>  set算法题练习

4.1  两个数组的交集

4.1.1  C++实现

4.1.2  图示:交集  &&  差集

4.1.3  博主手记

4.2  环形链表

4.2.1  C++实现

4.2.2  图示

4.2.3  博主手记

完整代码示例与实践演示

Test.cpp:

运行演示

结尾


C++的两个参考文档

老朋友(非官方文档):cplusplus

官方文档(同步更新):cppreference
set和multiset的参考文档:setmultiset


1  ~>  序列式容器和关联式容器

前面我们已经接触过STL中的部分容器比如:string、vector、list、deque、array、forward_list等,这些容器统称为序列式容器,因为逻辑结构为线性序列的数据结构,两个位置存储的值之间一般没有紧密的关联关系,比如交换一下,依旧是序列式容器。顺序容器中的元素是按他们在容器中的存储位置来顺序保存和访问的。

关联式容器也是用来存储数据的,与序列式容器不同的是,关联式容器逻辑结构通常是非线性结构,两个位置有紧密的关联关系,交换一下,他的存储结构就被破坏了。顺序容器中的元素是按关键字来保存和访问的。关联式容器有map/set系列和unordered_map/unordered_set系列。

这部分艾莉丝要介绍的map和set底层是红黑树,红黑树是一颗平衡二叉搜索树set是key搜索场景的结构,map是key / value搜索场景的结构。


2  ~>  了解set容器

2.1  set类介绍

1、set的声明如下,T就是set底层关键字的类型;

2、set对T要求比较大小,默认要求T支持小于比较的就可以了,如果不支持或者想按自己的需求走可以自行实现仿函数传给第二个模版参数;

3、set底层存储数据的内存是从空间配置器申请的,如果需要可以自己实现内存池,传给第三个参
数——这一点不需要管;

4、一般情况下,我们都不需要传后两个模版参数;

5、set底层是用红黑树实现的,红黑树我们已经知道是平衡二叉树,增删查效率是O(logN) ,迭代器遍历是走的搜索树的中序,左根右,所以是有序的;

6、前面部分我们已经介绍了vector / list等容器的使用,因为STL容器接口设计高度相似,所以这里我们就不再一个接口一个接口的介绍,而是直接带大家看文档,挑比较重要的接口进行介绍。

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

2.2  看set文档

set的支持正向和反向迭代遍历(至少是双向迭代器),遍历默认按升序顺序,因为底层是二叉搜索树,迭代器遍历走的中序;支持迭代器就意味着支持范围for,set的iterator和const_iterator都不支持迭代器修改数据,修改关键字数据,会破坏底层搜索树的结构。

2.2.1  set的构造和迭代器

// 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 & = allocator_type()); // copy (3) 拷贝构造 set(const set& x); // initializer list (5) initializer 列表构造 set(initializer_list<value_type> il, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); // 迭代器是一个双向迭代器 iterator->a bidirectional iterator to const value_type // 正向迭代器 iterator begin(); iterator end(); // 反向迭代器 reverse_iterator rbegin(); reverse_iterator rend();

2.2.2  拷贝构造

set的拷贝构造都是深拷贝,代价极大。


3  ~>​  set的使用层详解

3.1  增删查

3.1.1  增:insert插入

3.1.2  删:erase删除

erase是直接把值删掉——

3.1.3  查

3.1.4  单纯判断在不在:count

判断在不在很方便,在就返回0,不在返回非0——

3.1.5  lower_bound和upper_bound

3.2  insert和迭代器遍历

3.2.1  代码实现

set遍历完自带去重和有序(中序遍历)——

3.2.2  运行演示

3.3  find和erase

3.3.1  代码实现

用范围for打印一下删除之后的数组,对比一下——

3.3.2  运行演示

这里我们删除一个6看看——

3.4  multiset

3.4.1  和set之间的差异

multiset和set的使用基本完全类似,主要区别点在于multiset支持值冗余。

3.4.2  代码实现

// multiset void Test_set3() { multiset<int> s; s.insert(3); s.insert(4); s.insert(6); s.insert(5); s.insert(3); s.insert(6); s.insert(7); s.insert(9); s.insert(3); // 遍历结果:去重 set<int>::iterator it = s.begin(); while (it != s.end()) { //*it = 1; // 报错不准确,不是常量的问题,而是不能修改*it,it可以修改 cout << *it << " "; ++it; } cout << endl; // 查找中序遍历中第一个3 auto pos = s.find(3); while (pos != s.end() && *pos == 3) { cout << *pos << endl; pos++; } cout << endl; // [) —— 左闭右开 //std::pair<multiset<int>::iterator, multiset<int> ::iterator> ret = s.equal_range(3); //// 返回左闭右开的区间 auto ret = s.equal_range(3); cout << s.count(3) << endl; cout << s.erase(3) << endl; // 删除所有3 for (auto e : s) { cout << e << " "; } cout << endl; }

3.4.3  查找多个key

 比如查找3这个节点,如果有多个3节点,会查找中序遍历的第一个3节点——

3.4.4  equal_range

pair是库里面的类模板的结构,只是按struct公有定义——

左闭右开,pair(It1 , It2),里面是迭代器,但是只有在类内部才能写迭代器。

3.4.5  count函数

count函数的功能:指定值在迭代器范围内的出现次数。

3.4.6  运行演示

我们以删除3为例,运行一下——

3.5  交集和差集的作用:数据同步和数据备份

3.5.1  图示:数据同步和数据备份

3.5.2  博主手记

3.6  博主手记


4  ~>  set算法题练习

4.1  两个数组的交集

力扣链接:349. 两个数组的交集

力扣题解链接:使用哈希数组解决【两个数组的交集的问题】

题目描述:

4.1.1  C++实现

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> v; auto it1 = s1.begin(); auto it2 = s2.begin(); while(it1 != s1.end() && it2 != s2.end()) { if(*it1 < *it2) { it1++; } else if(*it1 > *it2) { it2++; } else{ v.push_back(*it1); ++it1; ++it2; } } return v; } };

4.1.2  图示:交集  &&  差集

4.1.3  博主手记

4.2  环形链表

力扣链接:LCR 022. 环形链表 II

力扣题解链接:

题目描述:

4.2.1  C++实现

/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *detectCycle(ListNode *head) { set<ListNode*> s; ListNode* cur = head; while(cur) { auto it = s.find(cur); if(it == s.end()) { s.insert(cur); // 不在,插入 } else { return *it; } cur = cur->next; } return nullptr; } };

4.2.2  图示

4.2.3  博主手记


完整代码示例与实践演示

Test.cpp:

#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<set> using namespace std; void Test_set1() { set<int> s; s.insert(1); s.insert(3); s.insert(4); s.insert(6); s.insert(5); s.insert(3); s.insert(6); // 遍历结果:去重(set)+ 有序 set<int>::iterator it = s.begin(); while (it != s.end()) { //*it = 1; // 报错不准确,不是常量的问题,而是不能修改*it,it可以修改 cout << *it << " "; ++it; } cout << endl; for (auto e : s) { cout << e << " "; } cout << endl; int x = 0; cin >> x; cout << s.erase(x) << endl; auto pos = s.find(x); // 查找返回下标 if (pos != s.end()) { s.erase(pos); } // 单纯判断在不在 if(s.count(x)) { } // 打印删除之后的数组 for (auto e : s) { cout << e << " "; } cout << endl; // 输出: // 1 3 4 5 6 // 1 3 4 5 6 // 输入: // 6 // 输出: // 1 // 1 3 4 5 } void Test_set2() { // 假设区间的最左段的3被注释掉了——直接从4开始 set<int> s; s.insert(1); //s.insert(3); s.insert(4); s.insert(6); s.insert(5); //s.insert(3); s.insert(6); s.insert(7); s.insert(9); for (auto e : s) { cout << e << " "; } cout << endl; // 删除[3,8]区间内的值 // 左开右闭 // >= 3 auto it1 = s.lower_bound(3); // > 8 auto it2 = s.upper_bound(8); s.erase(it1, it2); for (auto e : s) { cout << e << " "; } cout << endl; } // multiset void Test_set3() { multiset<int> s; s.insert(3); s.insert(4); s.insert(6); s.insert(5); s.insert(3); s.insert(6); s.insert(7); s.insert(9); s.insert(3); // 遍历结果:去重 set<int>::iterator it = s.begin(); while (it != s.end()) { //*it = 1; // 报错不准确,不是常量的问题,而是不能修改*it,it可以修改 cout << *it << " "; ++it; } cout << endl; // 查找中序遍历中第一个3 auto pos = s.find(3); while (pos != s.end() && *pos == 3) { cout << *pos << endl; pos++; } cout << endl; // [) —— 左闭右开 //std::pair<multiset<int>::iterator, multiset<int> ::iterator> ret = s.equal_range(3); //// 返回左闭右开的区间 auto ret = s.equal_range(3); cout << s.count(3) << endl; cout << s.erase(3) << endl; // 删除所有3 for (auto e : s) { cout << e << " "; } cout << endl; } int main() { //Test_set1(); //Test_set2(); Test_set3(); return 0; }

运行演示


结尾

往期回顾:

【C++:搜索二叉树】二叉搜索树从理论到实战完全解读:原理、两种场景下的实现

结语:都看到这里啦!那请大佬不要忘记给博主来个“一键四连”哦! 

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡

૮₍ ˶ ˊ ᴥ ˋ˶₎ა


Read more

Flutter for OpenHarmony: Flutter 三方库 hashlib 为鸿蒙应用提供军用级加密哈希算法支持(安全数据完整性卫士)

Flutter for OpenHarmony: Flutter 三方库 hashlib 为鸿蒙应用提供军用级加密哈希算法支持(安全数据完整性卫士)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在 OpenHarmony 应用开发中,涉及到本地存储加密、用户密码脱敏、大文件完整性校验或区块链应用时,一套算法完备、执行高效的哈希(Hash)库是必不可少的。虽然 Dart 原生也提供了一些简单的加密方法,但在算法多样性(如 Argon2、SHA-3, Blake2b 等高级算法)和性能表现方面,往往无法满足高安全等级项目的需求。 hashlib 正是专门为高性能数据加解密与完整性校验打造的库。它纯代码实现且经过了极致的循环优化,是鸿蒙平台上保护敏感数据的数字堡垒。 一、核心加密算法矩阵 hashlib 支持极其广泛且先进的加密标准。 原始明文数据 hashlib 算法引擎 传统算法 (MD5 / SHA-256) 现代算法 (SHA-3 / Keccak) 极致速度 (Blake2b / Blake2s) 密钥派生 (Argon2 / Scrypt)

By Ne0inhk
Redis核心数据结构与分布式锁实现详解

Redis核心数据结构与分布式锁实现详解

个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[[email protected]] 📱个人微信:15279484656 🌐个人导航网站:www.forff.top 💡座右铭:总有人要赢。为什么不能是我呢? * 专栏导航: 码农阿豪系列专栏导航 面试专栏:收集了java相关高频面试题,面试实战总结🍻🎉🖥️ Spring5系列专栏:整理了Spring5重要知识点与实战演练,有案例可直接使用🚀🔧💻 Redis专栏:Redis从零到一学习分享,经验总结,案例实战💐📝💡 全栈系列专栏:海纳百川有容乃大,可能你想要的东西里面都有🤸🌱🚀 目录 * Redis核心数据结构与分布式锁实现详解 * 一、Redis简介与数据结构概述 * 二、Redis常用数据结构详解 * 1. 字符串(String) * 2. 哈希(Hash) * 3. 列表(

By Ne0inhk
LeetCode128:哈希集合巧解最长连续序列

LeetCode128:哈希集合巧解最长连续序列

一、题目回顾 LeetCode 128 题「最长连续序列」是一道中等难度的数组题,核心要求如下:给定一个未排序的整数数组 nums,找出其中数字连续的最长序列(不要求序列元素在原数组中连续)的长度,且必须设计时间复杂度为 O (n) 的算法。 示例直观理解: * 输入 nums = [100,4,200,1,3,2],输出 4(最长序列是 [1,2,3,4]); * 输入 nums = [0,3,7,2,5,8,4,6,0,1],输出 9(完整连续序列 0-8)。 二、

By Ne0inhk
python~基础

python~基础

python~基础 * 1.python介绍 * 2.注释 * 3.波浪线提示 * 4.变量 * 4.1定义 * 4.2变量名命名规范 * 5.数据类型: * 5.1常见数据类型分类: * 5.2数据类型转换 * 6.交互运⾏ Python 代码: * 7.输入与输出 * 7.1 输入 * 7.2输出 * 7.2.1格式化输出 1.python介绍 python为解释型语言,解释器一边翻译一边执行,代码从上到下执行,如果下方代码出现错误,不会影响上方代码的执行 因为计算机只认二进制(0,1),所以需要解释器对代码进行翻译 怎么将python与自动化测试联系起来? Python + requests ->

By Ne0inhk