当前位置: 首页 > news >正文

C++—— set、map、multiset、multimap

目录

关联式容器

概念

键值对

树形关联式容器

set

介绍

定义方式

使用

map

介绍

使用

 multiset

介绍

使用

multimap

介绍

使用

相关的OJ题

前K个高频单词


关联式容器

概念

我们之前接触过的一些容器,比如:vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。
 

键值对

键值对是关联式容器特殊的结构。

SGI-STL中关于键值对的定义:

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)
{}
};

树形关联式容器

map、set、multimap、multiset。这些都是树形关联式容器,因为他们的底层都是平衡搜索树(红黑树),容器中的元素都是有序的。

set

介绍

  1. set是按照一定次序存储元素的容器。
  2. 在set中,元素的value也标识它,并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const,因为底层是平衡二叉树,如果修改可能会破坏平衡二叉树的性质)。
  3. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。
  4. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代。
  5. set在底层使用平衡二叉树(红黑树)实现的。
  6. 与map/multimap不同,map/multimap中存储的是真正的键值对<key,value>,set中只放value,但在底层实际存放的是由<value,value>构成的键值对。
  7. set中插入元素时,只需要插入value即可,不需要构造键值对。
  8. set中的元素不可以重复(因此可以使用set进行去重,不可以重复的原因也是底层是平衡二叉树)。
  9. 使用set中的元素默认按照小于来比较。
  10. set中查找某个元素,时间复杂度为:log2 n
  11. set中的元素不允许修改(因为底层是平衡二叉树,如果修改的话很可能会破坏平衡二叉树的结构)。

注意:平衡二叉树(AVL)是一种特殊的搜索二叉树(BST);

定义方式

//构造空容器
set<int> s1;//拷贝构造set容器
set<int> s2(s1);//使用迭代器拷贝某一段内容
string str("abcdef");
set<char> s3(str.begin(),str.end());//比较方式指定为大于
set<int,greater<int>> s4;

使用

 Compare:比较容器,set默认按照小于来比较,如果要对自定义类型进行比较的时候可以传入自定义的比较容器。

详细见代码:

void test_set1()
{set<int> s;//插入s.insert(3);s.insert(2);s.insert(1);s.insert(1);s.insert(6);s.insert(7);s.insert(4);//利用迭代器去遍历set//set<int>::iterator it = s.begin();auto it = s.begin();while (it != s.end()){cout << *it << " ";it++;}cout << endl;//利用范围for去遍历for (auto e : s){cout << e << " ";}cout << endl;//查找->找到就返回该元素的迭代器 否则返回end//两种find的查找方式是不一样的//O(logN) 这个是根据树的特殊结构来查找的auto pos = s.find(3);//O(N) 暴力查找//auto pos = find(s.begin(), s.end(), 3);if (pos != s.end()){s.erase(pos);}//删除 //有就删没有就不操作//s.erase(4)像这种是有返回值的//删除成功就返回1删除失败返回0cout << s.erase(4) << endl;cout << s.erase(100) << endl;for (auto e : s){cout << e << " ";}cout << endl;//count 返回该值在set里面的个数//其实count对set意义不大//但是对multiset意义更大//cout << s.count(3) << endl;}

注意:这里只列举一些常用的set的操作。

map

介绍

  1. map是关联式容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
  2. 在map中,键值key通常用于排序和唯一的标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key和value通过成员类型value_type绑定在一起,并为其取别名为pair:typedef pair value_type;
  3. 在内部,map中的元素总是按照键值key进行比较排序的。
  4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
  5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
  6. map通常被实现为平衡二叉搜索树(红黑树)。

使用

 Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)。

int main()
{//定义一个空容器map<string, string> dict;//插入key和valuedict.insert(pair<string, string>("排序", "sort"));dict.insert(pair<string, string>("左边", "left"));dict.insert(pair<string, string>("右边", "right"));//operator[]dict["迭代器"] = "iterator";//插入+修改dict["insert"];//插入dict["insert"] = "插入";//修改cout << dict["insert"] << endl;//查找,如果不在就是插入//插入失败因为已经有key对应的值了//dict.insert(pair<string, string>("右边", "xxx"));//make_pair是一个函数模板 可以根据你传入的值来推断具体的类型dict.insert(make_pair("字符串", "string"));//map<string, string>::iterator it = dict.begin();auto it = dict.begin();while (it != dict.end()){//cout << (*it).first << ":" << (*it).second << endl;cout << it->first << ":" << it->second << endl;it++;}cout << endl;for (const auto& kv : dict){cout << kv.first << ": " << kv.second << endl;}//可以用来统计次数string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };map<string, int> countMap;for (auto& e : arr){map<string, int>::iterator it = countMap.find(e);if (it == countMap.end()){countMap.insert(make_pair(e, 1));//第一次遇见这个水果直接给对应的key的value值插入1}else{it->second++;}}//用for循环进行遍历for (const auto& kv : countMap){cout << kv.first << ": " << kv.second << endl;}return 0;
}

注意:map是支持[]操作符的。

operator[]的原理是:


用<key, T()>构造一个键值对,然后调用insert()函数将该键值对插入到map中如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器operator[]函数最后将insert返回值键值对中的value返回。
 

 multiset

介绍

  1. multiset是按照特定顺序存储元素的容器,其中元素是可以重复的。
  2. 在multiset中,元素的value也会识别它(因为multiset中本身存储的就是<value, value>组成的键值对,因此value本身就是key,key就是value,类型为T). multiset元素的值不能在容器中进行修改(因为元素总是const的),但可以从容器中插入或删除。
  3. 在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则进行排序。
  4. multiset底层结构是一个平衡二叉树(红黑树)。
  5. 与set的区别是,multiset中的元素是可以重复的,set中value是唯一的。

使用

void test_set2()
{multiset<int> s;//插入 可以插入重复的值s.insert(2);s.insert(1);s.insert(1);s.insert(3);s.insert(3);s.insert(3);s.insert(3);s.insert(3);s.insert(3);s.insert(6);s.insert(7);s.insert(1);//set<int>::iterator it = s.begin();auto it = s.begin();while (it != s.end()){cout << *it << " ";it++;}cout << endl;for (auto e : s){cout << e << " ";}cout << endl;//注意://用来验证,如果有多个相同的值的时候//find找到是中序遍历的第一个auto pos = s.find(3);while (pos != s.end()){cout << *pos << " ";pos++;}//两种find的查找方式是不一样的//O(logN)//auto pos = s.find(3);//O(N)/*auto pos = find(s.begin(), s.end(), 3);if (pos != s.end()){s.erase(pos);}*///有就删没有就不操作//s.erase(4)像这种是有返回值的//删除成功就返回1删除失败返回0/*cout << s.erase(4) << endl;cout << s.erase(100) << endl;for (auto e : s){cout << e << " ";}cout << endl;*///count 返回该值在set里面的个数//其实count对set意义不大//但是对multiset意义更大cout << s.count(3) << endl;cout << s.count(1) << endl;
}

注意:multiset的find与set有所不同,由于multiset中允许键值冗余,所以multiset如果查找成功的话返回的是中序遍历该元素所出现的第一次的迭代器。

set中的count没什么意义,但是在multiset中count非常有意义,它能很好的计算出该元素在multiset容器中的具体数量。

multimap

介绍

multimap与set和multiset的区别非常相像,在这里不多介绍。

multimap不支持[],因为multimap允许键值冗余,[]不能确切的知道我们要访问的是哪个值。

multimap中的key是可以重复的。

使用

int main()
{multimap<string, string> dict;dict.insert(make_pair<string, string>("left", "左边"));dict.insert(make_pair<string, string>("left", "左边"));dict.insert(make_pair<string, string>("right", "右边"));dict.insert(make_pair("string", "字符串"));for (const auto& kv : dict){cout << kv.first << ":" << kv.second << endl;}
}

相关的OJ题

前K个高频单词

题目地址:

力扣icon-default.png?t=N2N8https://leetcode.cn/problems/top-k-frequent-words/description/

 我们这里使用multimap非常契合,我们的思路就是,首先使用multimap特殊的性质(允许键值冗余,并且有两个键值)。两个键值,一个存放单词,一个存放次数。

记录完毕数据之后使用次数进行排序,如果有相同的就设置特殊的比较容器具进行字典序排序。

最后输出前十个。

class Solution {
public:vector<string> topKFrequent(vector<string>& words, int k){//为stable_sort来创建比较容器struct Compare{bool operator()(const pair<int,string>&l,const pair<int,string>& r){return l.first>r.first;}};map<string,int> mapCount;//巧妙的使用了[] //不仅将words中的单词给插入进map并且还统计了次数for(auto& kv: words){mapCount[kv]++;//访问key对应的value给value进行++}vector<pair<int,string>> v;//注意这里value与key换了位置为了保证排序的时候直接使用第一个键值进行排序for(auto& kv : mapCount){v.push_back(make_pair(kv.second,kv.first));}//切记不要使用sort 因为sort底层为快排(不稳定)//然而stable_sort底层为归并(稳定)stable_sort(v.begin(),v.end(),Compare());vector<string> ret;for(size_t i=0;i<k;i++){ret.push_back(v[i].second);}return ret;}
};

http://www.lryc.cn/news/44354.html

相关文章:

  • Qlib使用
  • TL-WDR7660 httpProcDataSrv任意代码执行漏洞复现分析
  • 基于DDS的SOA测试方案实现
  • LibTorch中Windows系统环境配置及CUDA不可用问题解决
  • Java并发编程实战二
  • Linux中最基本的命令ls的用法有哪些?
  • 第 100002(十万零二)个素数是多少?
  • Lua迭代器
  • 同步与互斥之信号量
  • 如何当个优秀的文档工程师?从 TC China 看技术文档工程师的自我修养
  • 如何学习k8s
  • 【SSM】MyBatis(十.动态sql)
  • 最近很多人都在说 “前端已死”,讲讲我的看法
  • 大家好,我是火旺技术
  • 【Java并发编程系列】全方位理解多线程几乎包含线程的所有操作哦
  • 天宝S6测量机器人/天宝S6全站仪参数/教程/Trimble 天宝全站仪
  • c++基础知识汇总
  • 重磅!基于GPT-4的全新智能编程助手 GitHub Copilot X 来了!
  • 第04章_运算符
  • Excel 文件比较工具:xlCompare 11.0 Crack
  • 802.1x认证原理
  • GPIO的八种模式分析
  • 携职教育:财会人常用必备,203个EXCEL快捷键汇总
  • 【美赛】2023年ICM问题Z:奥运会的未来(思路、代码)
  • CSS基础入门
  • 可重入锁、读写锁、邮戳锁 详解
  • HBase客户端、服务器端、列簇设计、HDFS相关优化,HBase写性能优化切入点,写异常问题检查点
  • DINO-DETR在COCO缩减数据集上实验结果分析
  • 语聊房app源码及架构设计
  • 什么是软件测试?5分钟带你快速了解!