前情提要

根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树) 作为其底层结果,容器中的元素是一个有序的序列。他们都是底层都是通过<key, value>结构的键值对来存储的

C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构操作。vector封装数组,list封装了链表,map和set封装了二叉树等,在封装这些数据结构的时候,STL按照程序员的使用习惯,以成员函数方式提供的常用操作,如:插入、排序、删除、查找等。让用户在STL使用过程中,并不会感到陌生

set认识及使用

set中的key就是value,就是key=value的一种关联容器。也就是说存的内容不能一样,set具有天然的去重功能

特性

  1. set容器中不能存在重复元素,底层二叉树不允许存在重复元素
  2. 使用set的迭代器遍历set中的元素,可以得到有序序列,底层通过中序遍历得到
  3. set中插入元素时,只需要插入value即可,不需要构造键值对
  4. set中的元素不允许修改,二叉搜索树不允许修改

模板参数


T:set存放的数据类型
Compare:set元素存储顺序,默认less,为从小到大
Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理

构造函数

  1. explicit set (const key_compare& comp = key_compare(),
    const allocator_type& alloc = allocator_type());
  2. template < class InputIterator >
    set (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
void test()
{vector<int> vec = { 4, 3, 9, 6, 3 };set<int> set1; //空setset<int> set2 = { vec.begin(), vec.end()};//通过迭代器创建set容器set<int> ::iterator it = set1.begin();//set迭代器while (it != set1.end()){cout << *it << " ";//*it = 10; error 不允许修改++it;}
}

测试:保存5个元素,实际保存4个元素,重复元素不保存,迭代器的遍历就是按照树的中序遍历得到的

大小容量

  • size() 求set元素个数
  • empty() 判断set是否为空

插入

pair<iterator,bool> insert (const value_type& val);
返回值pair<iterator,bool>插入数据的位置以及插入是否成功的数据

void test()
{vector<int> vec = { 4, 3, 9, 6, 3 };set<int> set2 = { vec.begin(), vec.end() };//通过迭代器创建set容器//ret.second表示pair对象第二个数据,ret.first表示pair对象第一个数据pair<set<int>::iterator, bool> ret = set2.insert(7);cout << ret.second << " " << *ret.first << endl;//插入成功,pair第一个数据是新数据的迭代器ret = set2.insert(7);cout << ret.second << " " << *ret.first << endl;//插入失败,pair第一个数据是已经存在的数据的迭代器
}

测试:

iterator insert (iterator position, const value_type& val);
该接口并非是说在该迭代器的位置前插入数据,其实和上一个插入是一样的

void printSet(const set<int>& set)
{for (auto& s : set)cout << s << " ";cout << endl;
}void test()
{vector<int> vec = { 4, 3, 9, 6, 3 };set<int> set2 = { vec.begin(), vec.end() };printSet(set2);auto it = set2.end();set2.insert(it, 1);printSet(set2);
}

测试:

删除

size_type erase (const value_type& val);
返回值是删除的个数,存在则删除并返回1,不存在返回0

void erase (iterator position);

void erase (iterator first, iterator last);

void test()
{vector<int> vec = { 4, 3, 9, 6, 3, 10, 12, 50, 1 };set<int> set2 = { vec.begin(), vec.end() };set<int> ::iterator it = set2.begin();int ret = set2.erase(6);//删除成功返回1printSet(set2);cout << ret << endl;ret = set2.erase(5);//删除失败返回0printSet(set2);cout << ret << endl;set2.erase(it);//注意不能传非法位置:end()printSet(set2);set2.erase(++set2.begin(), --set2.end());//删除只剩头和尾两个数据printSet(set2);
}

测试:

查找

iterator find (const value_type& val) const;
找到返回数据的迭代器,找不到返回set容器的end()迭代器

void test()
{vector<int> vec = { 4, 3, 9, 6, 3, 10, 12, 50, 1 };set<int> set2 = { vec.begin(), vec.end() };set<int> ::iterator it = set2.begin();auto it = set2.find(3);cout << (it != set2.end()) << endl;auto it = set2.find(2);cout << (it != set2.end()) << endl;
}

测试:

size_type count (const value_type& val) const;
查看是否存在某个数据

void test()
{vector<int> vec = { 4, 3, 9, 6, 3, 10, 12, 50, 1 };set<int> set2 = { vec.begin(), vec.end() };cout << set2.count(4) << endl;cout << set2.count(40) << endl;
}

测试:

其他

set默认是一个递增的序列,我们可以手动修改成递减的序列

void printSet(const set<int, greater<int>>& set)
{for (auto& s : set)cout << s << " ";cout << endl;
}void test()
{vector<int> vec = { 4, 3, 9, 6, 3, 10, 12, 50, 1 };set<int, greater<int>> set2 = { vec.begin(), vec.end() };printSet(set2);
}

测试:

map

map是STL的一个关联容器,它提供一对一的hash。自动建立key - value的对应。key 和 value可以是任意你需要的类型,包括自定义类型。
特性

  1. 并且在map的内部,key与value通过成员类型value_type绑定在一起, 为其取别名称为pair
  2. map中的元素总是按照键值key进行比较排序的,默认为递增排序
  3. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value
  4. map中的key不可以重复,但是value可以重复。不能修改key,但是可以修改value

模板参数


Key:key值的类型
T:value值的类型

构造函数

explicit map (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); 空构造

template < class InputIterator>
map (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), 通过迭代器创建map容器

template <class T1, class T2>
void printMap(const map<T1, T2>& m)
{//map中的数据pair,不能直接解引用获得值typename map<T1, T2>:: const_iterator it = m.begin();while (it != m.end()){cout << it->first << "-->" << it->second << endl;++it;}
}void test()
{map<int, int> map1;//空的map容器pair<int, int> arr[] = {pair<int ,int>(1, 3), pair<int ,int>(1, 2) ,pair<int ,int>(3, 3) , pair<int ,int>(0, 0), pair<int ,int>(5, 5) };map<int, int> map2(arr, arr+sizeof(arr) / sizeof(arr[0]));//通过数据创建迭代器printMap(map2);
}

测试:key值一样不能存入。迭代器的访问底层也是一个中序遍历,按照key的递增序列的遍历

map的迭代器可以修改value,但是不可以修改key

void test()
{pair<int, int> arr[] = { pair<int ,int>(1, 3), pair<int ,int>(1, 2) ,pair<int ,int>(3, 3) , pair<int ,int>(0, 0), pair<int ,int>(5, 5) };map<int, int> map1(arr, arr+sizeof(arr) / sizeof(arr[0]));printMap(map1);cout << "修改第一个数据的value" << endl;auto it = map1.begin();//第一个数据it->second = 10;//it->first = 2; errorprintMap(map1);
}

测试:

[]方括号

mapped_type& operator[] (const key_type& k);
和我们平时使用数组时使用方法类似,key相当于索引,value就是索引对应的值 map[key] == value

void test()
{pair<int, int> arr[] = { pair<int ,int>(1, 3), pair<int ,int>(1, 2) ,pair<int ,int>(3, 3) , pair<int ,int>(0, 0), pair<int ,int>(5, 5) };map<int, int> map1(arr, arr + sizeof(arr) / sizeof(arr[0]));printMap(map1);cout << map1[1] << endl;map1[1] = 100;cout << map1[1] << endl;
}

测试:

但是map中的[]具有新的功能,即使key不存在,也可以通过[]来插入新的一个键值对并输出对应的value值

void test()
{pair<int, int> arr[] = { pair<int ,int>(1, 3), pair<int ,int>(1, 2) ,pair<int ,int>(3, 3) , pair<int ,int>(0, 0), pair<int ,int>(5, 5) };map<int, int> map1(arr, arr + sizeof(arr) / sizeof(arr[0]));map1[10] = 100;//插入一个key为10,value为100的数据cout << map1[10] << endl;printMap(map1);
}

测试:

我们通过这个接口底层实现来理解为什么可以通过[]来插入数据
其实map进行[]操作就等于进行对下面这行代码进行操作

(*((this->insert(make_pair(k,mapped_type()))).first)).second

我们来分解一下

1.先执行mapped_type(),也就是value类型的缺省值

2.执行make_pair(k,mapped_type()),创建一个键值对pair对象

3.执行insert(make_pair(k,mapped_type())), 插入这个创建好的pair对象,并返回pair<iterator,bool>,其中iterator是新插入的key对应迭代器

4.执行(this->insert(make_pair(k,mapped_type()))).first,调用返回的pair<iterator,bool>第一个元素iterator

5.执行*((this->insert(make_pair(k,mapped_type()))).first),对迭代器进行解引用获得pair对象pair<int, int>

6.执行(*((this->insert(make_pair(k,mapped_type()))).first)).second,调用pair对象的第二个元素,也就是value

如果key值已经存在,insert插入失败会返回已经存在的pair< iterator, bool>,其中iterator是已经存在的键为key的pair的迭代器

at接口

mapped_type& at (const key_type& k);

void test()
{pair<int, int> arr[] = { pair<int ,int>(1, 3), pair<int ,int>(1, 2) ,pair<int ,int>(3, 3) , pair<int ,int>(0, 0), pair<int ,int>(5, 5) };map<int, int> map1(arr, arr + sizeof(arr) / sizeof(arr[0]));//at接口和operator[]的区别就在于at执行插入对象存在时抛异常cout << map1.at(10) << endl;
}

insert

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

void test()
{map<int, int> map1;auto ret = map1.insert(pair<int, int>(1, 1));cout << ret.first->first << "-->" << ret.first->second << "insert:" << ret.second << endl;ret = map1.insert(make_pair(2, 2));cout << ret.first->first << "-->" << ret.first->second << "insert:" << ret.second << endl;//key值存在插入失败ret = map1.insert(make_pair(2, 3));cout << ret.first->first << "-->" << ret.first->second << "insert:" << ret.second << endl;
}

测试:

erase

size_type erase (const key_type& k);

void test()
{pair<int, int> arr[] = { pair<int ,int>(1, 3), pair<int ,int>(1, 2) ,pair<int ,int>(3, 3) , pair<int ,int>(0, 0), pair<int ,int>(5, 5) };map<int, int> map1(arr, arr + sizeof(arr) / sizeof(arr[0]));size_t num = map1.erase(3);//存在返回1cout << num << endl;num = map1.erase(2);//不存在返回0cout << num << endl;
}

测试:

multiset

与set类似,但是存的值可以重复
特性

  1. multiset是按照特定顺序存储元素的容器,其中元素是可以重复的。
  2. multiset元素的值不能在容器中进行修改(因为元素 总是const的),但可以从容器中插入或删除
  3. 默认排序依然是递增

操作

void test()
{int arr[] = { 1, 6, 8, 5, 4, 9, 2, 4, 9 };multiset<int> ms(arr, arr + sizeof(arr) / sizeof(arr[0]));for (auto& e : ms){cout << e << " ";}cout << endl;
}

multimap

与map类似,但是存的key值可以重复
特性

  1. Multimaps是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对<key, value>,其中 多个键值对之间的key是可以重复的
  2. 默认排序依然是递增

操作

void test()
{multimap<int, int> mm;mm.insert(make_pair(10, 10));mm.insert(make_pair(20, 20));mm.insert(make_pair(10, 30));mm.insert(make_pair(10, 40));mm.insert(make_pair(10, 50));mm.insert(make_pair(50, 60));mm.insert(make_pair(40, 70));mm.insert(make_pair(30, 80));for (auto& e : mm){cout << e.first << "-->" << e.second << endl;}
}

测试:multimap不存在[]的操作

练习题

题目

思路
0、示例如下

1、将单词存入到map<string ,int>中,此时单词在mp中的存的是按照字符升序,且带有出现频率的map容器

2、创建一个,multimap容器,类型为multimap<int ,string, greater< int>>。将mp中的key存在mf中的value中,将mp中的value存在mf中的key中

3、将mf中的value存到数组中并返回

代码

class Solution {public:vector<string> topKFrequent(vector<string>& words, int k) {//string:单词 int:出现次数map<string, int> mp;//安装字符顺序排序,递增序列for (const auto& wd : words){mp[wd]++;}//multimap的key可以重复,但不支持[]//int:出现次数 string单词, 安装出现次数大小排序multimap<int, string, greater<int>> mf;//字母升序遍历for (const auto& p : mp){//将mp中的出现次数和单词插入到mf中//即使单词出现频率相同,也是先插入的在前面,也就是字母小的在前//满足优先按照频率排序,再安装字符升序排序mf.insert(make_pair(p.second, p.first));}vector<string> res;for (const auto& p : mf){if (k == 0)break;else{res.push_back(p.second);--k;}}return res;}
};

C++ 关联容器set | map | multiset | multimap相关推荐

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

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

  2. C++知识点32——使用C++标准库(关联容器set和multiset的初始化,赋值,查找,添加,删除与迭代器失效)

    关联容器map和multimap已经在博客https://blog.csdn.net/Master_Cui/article/details/108690877和https://blog.csdn.ne ...

  3. set | map | multiset | multimap 快速上手

    map和set 1.关联式容器 序列式容器: 在初阶,我们接触过STL部分容器,如: vector.list.deque.forward_list(C++11)等,这些容器被称为序列式容器. 原因:这 ...

  4. C++知识点30——使用C++标准库(关联容器map及其初始化,赋值,查找,添加,删除与迭代器失效)

    一.关联容器简介 关于顺序容器和关联容器的区别已经在博客https://blog.csdn.net/Master_Cui/article/details/107427911中提过 C++标准库中的关联 ...

  5. C++ ——一文读懂:关联容器

    文章目录 使用关联容器 关联容器概述 定义关联式容器 pair类型 关联容器操作 关联容器迭代器 添加元素 删除元素 map的下标操作 访问元素 无序容器 管理桶 标准库提供8个关联容器: 类型map ...

  6. 200922阶段一C++关联容器map

    目录 一.学习的知识点 关联容器 map 二.上课没有听懂或者没有理解的地方 三.当天学习的收获 四.作业的思路.不会的地方 五.其他需要反馈的问题 一.学习的知识点 关联容器 通过保存在数据项中的索 ...

  7. C++ 11 特性:关联容器map、set的使用

    参考文献<C++ Primer> 一.关联容器概述 1.1 关联容器的概念 关联容器支持高效的查找与访问,主要的关联容器为map与set这两个.其中map主要提供的是键-值的操作,比如字典 ...

  8. 算法训练营 训练 硬木种类、双重队列、水果(关联容器map)

    关联容器:map map的键和值可以是不同的类型,键是唯一的,每个键都对应一个值.map可被当做希哈表使用,它建立了从键(关键字)到值的映射.map是键和值的一一映射,使用时需要引入头文件#inclu ...

  9. 第十二篇:实用的关联容器

    前言 我们可以用下标访问顺序容器的元素,也就是说在顺序容器实现中下标和元素的值相关联.那么能不能让别的值(而不是下标)与元素的值相关联呢?有的,实现这种功能的容器就叫做关联容器,而关联的本质就是某个特 ...

最新文章

  1. 互联网元年:如何提升自己?
  2. iBatis.Net(C#)数据库查询
  3. 看雪 2016CrackMe 攻防大赛 - 1-Crack_Me-凉飕飕
  4. [转载] Python-类变量,成员变量,静态变量,类方法,静态方法,实例方法,普通函数
  5. 计算机开机无法定位,开机提示无法定位程序输入点...于动态链接库CommFunc.dll上...
  6. 《JAVA与模式》— 原型模式
  7. iOS 测试app提示不受信任的开发者
  8. centos下安装transmission下载工具
  9. 四大名著地图上线,邀您一起来体验!
  10. android如何加载一张大图片,Android 实现加载大图片的方法
  11. 在Ubuntu 12.10中安装QQ2012
  12. HTML5期末大作业:旅游网页设计与实现——旅游风景区网站HTML+CSS+JavaScript 景点静态网页设计 学生DW静态网页设计
  13. 配色工具Material Design
  14. Android答题计时的代码,Android答题倒计时
  15. 逆向某视频直播软件,破解收费观看
  16. 关于ios::sync_with_stdio(false);和 cin.tie(0)加速c++输入输出流
  17. RabbitMQ解决消息幂等性问题
  18. 搜狗怎么做收录?我们来看看搜狗官方的回答
  19. 基于sqlite3的利用数据库实现简单通讯录
  20. 轻松解读仿生学最优化算法(一)——遗传算法

热门文章

  1. php静态stitac,php静态static介绍
  2. java类输出_java的输出类
  3. 10g 回收站(RECYCLE BIN)导致查询表空间的利用率时很慢
  4. oracle 查看隐含参数的脚步
  5. Oracle11g新特性:在线操作功能增强-表增加包含默认值的字段(转载)
  6. swagger 扫描java文档_使用Javadocs生成Swagger文档
  7. 华为热设计工程师待遇_沃得分享 | 华为校招青睐哪些江浙沪高校?
  8. js 判断字符串为空的方法
  9. 最新android proguard下载
  10. 心理软件测试自学,软件测试中的心理学