map和set

1.关联式容器

序列式容器:
在初阶,我们接触过STL部分容器,如: vector、list、deque、forward_list(C++11)等,这些容器被称为序列式容器。

原因:这些容器底层结构为线性序列的数据结构,里面存储的是元素本身。

关联式容器:
关联式容器也是用来存储数据的。

原因:关联式容器序列式容器不同的是,其里面存储的是<key,value>结构的键值对,在数据检索时比序列式容器效率更高,因为数据间具有关联性

2.键值对

概念:用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表剑指,value表示与key对应的信息。
比如:英汉互译的字典,每个字典中的英文单词与其中文含义保持一一对应的关系,就是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)
{}
};

3.树形结构的关联式容器

根据场景不同,STL总共实现了两种不同结构的管理式容器:树形结构与哈希结构。
树形结构的关联式容器:map、set、multimap、multiset。
该四种容器的共同点:使用平衡搜索树(即红黑树)作为底层结构,容器中的元素是一个有序的序列。

3.1 set

3.1.1 介绍

3.1.2 特点

  • set是按照一定次序存储元素的容器

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

  • 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行
    排序。

  • set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对
    子集进行直接迭代。

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

注意:

  • 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放
    value,但在底层实际存放的是由<value, value>构成的键值对。

  • set中插入元素时,只需要插入value即可,不需要构造键值对。

  • set中的元素不可以重复(因此可以使用set进行去重)。

  • 使用set的迭代器遍历set中的元素,可以得到有序序列

  • set中的元素默认按照小于来比较

  • set中查找某个元素,时间复杂度为:O(logN)

  • set中的元素不允许修改(为什么?),因为修改之后就不满足AVL树或红黑树的条件了。

  • set中的底层使用二叉搜索树(AVL树或红黑树)来实现

3.1.3 用例

#include <iostream>
#include <set>
using namespace std;
void TestSet()
{/*用数组array中的元素构造set*/int array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 2, 4,6, 8, 0 };int sz = sizeof(array) / sizeof(array[0]);int i = 0;printf(" 用数组array中的元素构造set\n");for (i = 0; i < sz; i++)cout << array[i] << ' '; cout << endl;set<int> s(array, array + sizeof(array) / sizeof(array[0]));//迭代器(或原生指针)区间构造printf("set中元素个数\n");cout << s.size() << endl;/* 正向打印set中的元素,从打印结果中可以看出:set可去重*/printf(" 正向打印set中的元素,从打印结果中可以看出:set可去重\n");for (auto& e : s)cout << e << " ";cout << endl;/* 使用迭代器逆向打印set中的元素*/printf(" 使用迭代器逆向打印set中的元素\n");for (auto it = s.rbegin(); it != s.rend(); ++it)cout << *it << " ";cout << endl;/* set中值为3的元素出现了几次*/printf(" set中值为3的元素出现了几次\n");cout << s.count(3) << endl;
}
int main()
{TestSet();return 0;
}

3.2 map

3.2.1 介绍

3.2.2 特点

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

3.2.3 用例

3.2.3.1 统计计数
//一、统计次数//法1.迭代器map<string, int> countMap;for (auto& str : arr){map<string, int>::iterator it = countMap.find(str);if (it != countMap.end()){//(*it).second++;it->second++;//本质是it-> 先找到Node; 再(it->)->second;找到second;但编译器优化成了it->second;//不能再写(it->)->second了}else{countMap.insert(make_pair(str, 1));}
}//法2.map的[]重载,以前是支持数组(顺序表)等连续的存储空间的结构使用。// map的[]的意思变,返回key对应的value的引用。countMap[key]=value;map<string, int> countMap;for (auto& str : arr){// 1、str不在countMap中,插入pair(str, int()),然后在对返回value的引用,次数++// 2、str在countMap中,返回value(次数)的引用,次数++;countMap[str]++;}

3.2.4 成员函数/方法

3.2.4.1 operator[]重载

特点

  • map中有这个key时,返回value的引用。(查找,可修改value)
  • map中没有这个key时,会插入一个pair(key,V()),并返回value的引用。-其中V()是value类型匿名对象,默认为空。(插入,可修改value)

用例

 map<string, string> dict;dict.insert(make_pair("sort", "排序"));dict["insert"];//如果不插入对应的value,则默认插入一个value()dict["insert"] = "插入";dict["left"] = "左边";

模拟实现

mapped _type& operator[](const key_type& k)
{//本质是return (*((this->insert(make_pair(k,mapped_type()))).first)).second;return (*((insert(make_pair(k,mapped_type()))).first)).second;
}//解读:
//一、insert解释
//single element (1)    pair<iterator,bool> insert (const value_type& val);
// 简化为:pair<K,V> insert(pair<K,V> val);
//1. key已经在map中,插入失败,返回pair(key_iterator,false);   即返回pair(已经有的key的迭代器,false);
//2.key不在map中,插入成功,返回pair(new_key_iterator,true);   即返回pair(新的key的迭代器,true);//二、return值的解释
//可拆分为
//pair<iterator,bool> ret=insert(make_pair(key,V()));插入成功返回的是新key的迭代器,插入失败返回的是已有key的迭代器。
//return ret.first ->second;   ret是pair<iterator,bool>。ret.first找到pair的iterator,iterator也是pair<key,value>类型的指针。再对(ret.first) ->second是找到迭代器的value。//疑问:
//为什么不用find+insert实现。因为find要遍历一遍查找,insert又要遍历一遍插入。所以能单纯insert就单纯insert,只需要遍历一遍插入。

3.3 multiset

3.3.1 介绍

是map的一种,在头文件map中,要#include 包含头文件。

3.3.2 特点

  • multiset是按照特定顺序存储元素的容器,其中元素是可以重复的。
  • 在multiset中,元素的value也会识别它(因为multiset中本身存储的就是<value, value>组成的键值对,因此value本身就是key,key就是value,类型为T). multiset元素的值不能在容器中进行修改(因为元素总是const的),但可以从容器中插入或删除。
  • 在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则进行排序。
  • multiset容器通过key访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭代器遍历时会得到一个有序序列。
  • multiset底层结构为二叉搜索树(红黑树)。

3.3.3 用例

用例与multimap类似,区别在于pair<key,value>中的value是空值,不对其进行赋值操作。

3.4 multimap

3.4.1 介绍

是map的一种,在头文件map中,要#include 包含头文件。

3.4.2 特点

  • 支持键值冗余,因为multiset是按照特定顺序存储元素的容器,其中元素是可以重复的。
  • 不支持operator[]重载,因为可能有多个key,[]无法知道返回哪个key的value。
  • 在multiset中,元素的value也会识别它(因为multiset中本身存储的就是<value, value>组成的键值对,因此value本身就是key,key就是value,类型为T). multiset元素的值不能在容器中进行修改(因为元素总是const的),但可以从容器中插入或删除。
  • 在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则进行排序。
  • multiset容器通过key访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭代器遍历时会得到一个有序序列。
  • multiset底层结构为二叉搜索树(AVL树或红黑树)。

3.4.3用例

 multimap<string, string> mdict;mdict.insert(make_pair("sort", "排序"));mdict.insert(make_pair("left", "左边"));mdict.insert(make_pair("left", "左边"));mdict.insert(make_pair("left", "剩余"));

3.5 面试题/编程题

  • 前K个高频词汇

题目链接:692. 前K个高频单词 - 力扣(LeetCode)

题目描述:

法一 优先级队列+仿函数

  struct Less//仿函数 stable_sort/sort传的less是排成升序,priority传的less是排成降序(大堆),注意区分。//即,sort传的less,若前<后为真则不需要交换,使的前<后。priority传的less,若前<后为真则需要交换,使得前>后。{bool operator()(const pair<string,int>& kv1,const pair<string,int>&kv2)const{if(kv1.second<kv2.second)//若kv1的次数second<kv2的次数second,则交换,把次数少的往后挪,次数多的往前挪return true;if(kv1.second==kv2.second&&kv1.first>kv2.first)//若次数相同,则kv1的字母值大于kv2的字母值,则交换return true;                                            //把字母小的往前挪,字母大的往后挪return false;}};vector<string> topKFrequent(vector<string>& words, int k) {map<string,int> countMap;//统计次数for(auto& str:words){countMap[str]++;//按字符大小顺序排一次序并且计数}//topk//法1.push构造 优先级队列// priority_queue<pair<string,int>,vector<pair<string,int>,Less>maxHeap;// for(auto& kv:countMap)// {//     maxHeap.push(kv);// }//法2.迭代器区间构造 优先级队列typedef  priority_queue<pair<string,int>,vector<pair<string,int>>,Less>MaxHeap;MaxHeap mh(countMap.begin(),countMap.end());//传Less优先级队列建大堆,即建起来之后是parent<child,//或者可以理解为:在建的时候只要满足 前<后,则swap。//结果:将大的往前挪,将小的往后挪。 即排完之后parent(后的)<child(前的)vector<string> v;while(k--){v.push_back(mh.top().first);//取topkmh.pop();}return v;}

法二 默认排序sort+稳定性仿函数 / stable_sort稳定性排序 (相同的值相对顺序不变)

//stable_sort/sort无法对map进行排序,sort要求是联系物理空间的迭代器,所以先得把map的数据放到vector里。struct Greater{bool operator()(const pair<string,int>& kv1,const pair<string,int>& kv2){if(kv1.second>kv2.second)//若为真则不需要交换return true;if(kv1.second==kv2.second&&kv1.first<kv2.first)//若为真,则不需要交换--//这个将默认的不稳定的快排变成了稳定的快排。或者也可以//把这个给注释掉,直接使用库里稳定的stable_sort排序return true;return false;}};vector<string> topKFrequent(vector<string>& words, int k) {map<string,int> countMap;//统计次数for(auto& str:words){countMap[str]++;//按字符大小顺序排一次序并且计数}vector<pair<string,int>> sortV(countMap.begin(),countMap.end());sort(sortV.begin(),sortV.end(),Greater());//也可以直接使用库里的stable_sort()进行排序,此时仿函数中可以不用考                                                  //虑相同值的先后顺序,因为stable_sort()会默认进行处理。vector<string> v;for(size_t i=0;i<k;i++){v.push_back(sortV[i].first);//取topk}return v;}

法三 multimap

  vector<string> topKFrequent(vector<string>& words, int k) {map<string,int> countMap;//统计次数for(auto& str:words){countMap[str]++;//按字符大小顺序排一次序并且计数。}multimap<int,string,greater<int>> sortMap;//不能使用map,因为map会去重,可能会舍去很多出现次数相同的单词。//multimap的greater和less是对于key来说的,也就是现在的int。按降序来排,和sort的less和greater含义相同,//若 前>后 为真,则不交换。使得大的在前,小的在后。//这里必须加个greater<int>,因为如果不加的话排出来的是默认按less<int>,这样排出来的multimap sortV是升序的。//要取次数topk大只能用反向迭代器在sortV里倒着去取,但是因为sortV里的字母的顺序是已经在前面计数+字符大小排序时排好的            //了,倒着去取的话会使字母的顺序逆序。是不行的。for(auto& kv:countMap){sortMap.insert(make_pair(kv.second,kv.first));//再按出现次数插入multimap排一次序,按出现次数排降序。}vector<string> v;multimap<int,string,greater<int>>::iterator it=sortMap.begin();for(size_t i=0;i<k;i++){v.push_back(it->second);//取topkit++;} return v;}
  • 两个数组的交集

题目链接:https://leetcode.cn/problems/intersection-of-two-arrays/submissions/

题目描述:

法一 不相同,则小的++;相等,则同时++,并插入vector。最后的vector即为交集。

 vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {set<int> s1(nums1.begin(),nums1.end());//默认构造set里已经排好序,升序。set<int> s2(nums2.begin(),nums2.end());auto it1=s1.begin();auto it2=s2.begin();vector<int> v;while(it1!=s1.end()&&it2!=s2.end()){//让小的++,因为小的一定不属于交集。if(*it1<*it2)//不相等的则为差集{it1++;}else if(*it2<*it1){it2++;}else//找到相等才是交集。{v.push_back(*it1);it1++;it2++;}}return v;}

set | map | multiset | multimap 快速上手相关推荐

  1. C++ 关联容器set | map | multiset | multimap

    前情提要 根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构.树型结构的关联式容器主要有四种:map.set.multimap.multiset.这四种容器的共同点是:使 ...

  2. c++中map、multimap、unordered_map、unordered_multimap的区别

    前言: c++的各种容器使用的时候很方便,但是如果作为一个初学者,看到一堆库要记住也是很头疼的,而且很多库名称会很相似,所以我们要很好的使用这些库的时候,我们需要了解清楚它们底层实现的原理,这样我们使 ...

  3. C++_STL——map、multimap、set、multiset

    C++_STL--map.multimap.set.multiset 内部都由红黑树实现 这里专栏里其他文章提到的函数(方法)就不会再说 参考:cplusplus 有序哈希表 有序不可重复哈希表(映射 ...

  4. STL之set map 和multiset multimap理解

    1.set multiset底层及区别? 都是由二叉搜索树(红黑树实现),不同在于multiset允许键值冗余. 2.map 和 multimap底层及区别? 都是由红黑树实现,不同在于multima ...

  5. map multimapc++_C++的Map和Multimap

    广州C++培训的小编这一期给大家讲Map和Multimap. 6.6 Maps和Multimaps map和multimap将key/value pair当作元素进行管理.他们可根据key的排序准则自 ...

  6. Spark快速上手-WordCount案例

    在此之前,我已经用MapReduce 框架实现了WordCount案例,接下来,我开始学习数据处理的另外一个非常重要的方法:Spark.首先,使用WordCount案例实现Spark快速上手. 创建M ...

  7. Forth Week :快速上手一门编程语言

    快速上手一门编程语言 概述 图灵完备性语言 识别类型 学习路径 一 掌握该语言的背景 - 2h 二 掌握该语言的基本程序逻辑语法 - 1h 1.顺序结构 2.分支结构 1)if语句 2)switch ...

  8. c++STL容器的Map和multimap

    STL容器的Map和multimap map/multimap的简介 map/multimap对象的默认构造 map的插入与迭代器 迭代器遍历 map对象的拷贝构造与赋值 map的大小 map的删除 ...

  9. mysql快速上手3

    上一章给大家说的是数据库的视图,存储过程等等操作,这章主要讲索引,以及索引注意事项,如果想看前面的文章,url如下: mysql快速上手1 mysql快速上手2 索引简介 索引是对数据库表中一个或多个 ...

最新文章

  1. 新手入门:Kaggle NLP比赛总结
  2. Navicat 2003-can't connect to MYSQL server on 'localhost'(10061)
  3. ffmpeg 如何把左右声道_耳机里的乾坤 | 左右声道?耳返?这些耳机常识,爱听音乐的你一定不会错过...
  4. axure中的拐弯箭头_Axure 8.0制作水平方向上一直来回移动的箭头
  5. 简化得最没道理的6个汉字,让人大跌眼镜
  6. 怎么修改html游戏存档,星露谷物语存档修改图文教程 怎么修改游戏数据
  7. 联发科被动“卡位”内地集成电路市场 剑指老对手展讯
  8. 《CSS世界》(张鑫旭)pdf
  9. 单片机原理及接口技术第1章
  10. java rrd 读取_rrd4j的使用详解1–数据保存入rrd文件 | 学步园
  11. Linux下的MongoDB基础学习二
  12. 阅读笔记:XModal-ID: Through-Wall Person Identification from Candidate Video Footage Using WiFi
  13. 有一种空格叫做--不间断空格(空格保存到数据库变成了问号)
  14. 创建题库后Excel导入试题老是失败?人工导题服务上线啦~
  15. 2019劳动节出游数据_这个劳动节周末要流什么
  16. Python中文件路径
  17. 虚拟机与主机ssh连接
  18. 张孝祥整理Java就业面试题大全
  19. Access根据出生日期计算年龄_不好意思,Power Query里根据出生日期计算年龄有点儿繁琐!...
  20. 今日测试xtr116郁闷到了

热门文章

  1. 电机、 电源等多条电路的接线方法
  2. linux系统怎么清理,linux系统怎么清理垃圾清理
  3. 华为查看存储正在计算机,华为手机怎么看内存使用情况
  4. 在MATLAB中如何读取心音信号,基于Matlab的心音信号分析比较研究_问答库
  5. QlikView AutoNumber函数
  6. BUUCTF pwn wp 76 - 80
  7. mysql的默认密码_mysql默认密码是多少
  8. mysql 存储utf8_Mysql以utf8存储gbk输出的实现方法
  9. android one s8,非官方的LineageOS 17.1将安卓10带入三星Galaxy S8和Note 8
  10. json 报错 There is a cycle in the hierarchy!