STL容器_map与set
Posted 楠c
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STL容器_map与set相关的知识,希望对你有一定的参考价值。
目录
1. 序列式容器与关联式容器
之前学的vector、list、deque都是序列式容器,在逻辑结构上是线性表。(栈和队列是容器适配器)而今天所学习的map与set是一棵搜索树,它叫做树型结构关联式容器,之后会学习的哈希表他是一种哈希结构关联式容器。
关联式容器:
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高
树型结构的关联式
容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层实现,容器中的元素是一个有序的序列。
2. set与multiset
T: set中存放元素的类型,实际在底层存储<value, value>的键值对。
Compare:set中元素默认按照小于来比较
Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理
后两个是缺省的,所以我们可以自定义其实现。
set本身虽然底层是红黑树即平衡搜索二叉树,由于搜索树的中序是有序的,所以他输出也是有序的。
2.1 增删查
字符串也是一样的。
再来看删除,首先Find一个迭代器指针,那么是使用set里的find,还是算法里的find呢?
Find到了之后就可以进行erase,但是erase依旧会引起迭代器失效
依旧和以前一样使用返回值解决
const类型不能修改。
所以set的特点是排序+去重。那如果是想要排序不去重呢?这时候就用到multiset
这时他的count函数,统计k值,就有了意义,因为multiset的k值可以重复。
同时也引入一个问题,find也是按照k值去找,那他找到哪一个呢,它找的是中序的第一个
3. map
很经典的k/v模型。
K为key_type
,T为mapped_type
。
而 Pair<const key,T>
被typedef为value_type
3.1 插入
3.1.1 模板参数
value_type是pair<const key_type,mapped_type>
,
3.1.2 返回值
假设对应的k不在,插入成功,并返回该位置的迭代器,假如k存在,插入失败,返回k对应的迭代器。
3.1.3 用法
不过几乎不用它的返回值。
插入时可以构造一个pair匿名对象插入,也可以构造一个pair对象。
迭代器输出时,重载了运算符->,所以用它输出first与second。
范围for输出时,拿到的就是dict里的pair对象,所以直接用.输出 。
3.1.4 排序,去重,统计次数
其中用到了make_pair
countMap.insert(pair<string,int>(e,1));
等于
countMap.insert(make_pair(e,1));
其实make_pair是对pair的一层封装。
减少了模板参数,和带上命名空间的繁琐,加上inline也不会有函数调用所产生的性能损失。
回过头来,看实验现象,统计出了次数,但实际上没有这么麻烦,那就是用【】。
3.1.5 运算符重载[ ]
[]也是封装了insert
只用一行代码解决了。
他的[ ],其实是这一样一个封装了insert函数
所以map[]有两层作用
- 假如k不在,会插入pair<k,v()>,并且返回类型的缺省值val,而且返回的是引用。
- k存在,不插入,会返回与k相等的那个节点的引用。
3.2 查找与删除
3.2.1 模板参数
k值
迭代器,k值,迭代器区间
3.2.2 返回值
- 返回k值的迭代器,假如没找到会返回,
map,end()
- size_type:一种无符号整数类型,可以表示差分类型的任何非负值
3.2.3 用法
find
erase
删除一个不存在的值,竟然没报错
那么删除一个不存在的迭代器试一下?感叹自己昏了头,那个迭代器没找到,返回的是countMap.end(),删除肯定段错误了。
假如存在的话,find找到对应的迭代器,然后删除对应元素
但是有个迭代器失效问题需要注意,it不能直接用,需要用返回值接收重新被赋值后才能使用,因为在函数内部他指向了下一个元素,在外面还指向那块已经删除的空间,也需要改变。
4. multimap
几乎和map用法差不多,他没有[ ],因为k值重复。
假如k值有重复,find找出中序的第一个。
5. 应用
5.1 找出最受欢迎的水果
补充一下sort其中的一个用法。
第一个随机迭代器,那么就只有vector,list,deque,支持了。
数据量小时:
- 排序,取出前k个.(但要注意排序不稳定)
- 大堆,取k次堆顶。
数据量大时: - 建k个数的小堆
- map去重,按asii码值排升序。插入到降序的mutimap中,用k比较,所以拿次数作为multimap的k。对象就在
其中按次数降序排好。
排序:
class com{
public:
bool operator()(count_it &x, count_it &y)
{
return x->second > y->second;
}
};
void testFriut3(const vector<string>& str, int k)
{
map<string, int> count_map;
for (auto& e : str)
{
count_map[e]++;
}
vector<count_it> v;
count_it it = count_map.begin();
while (it != count_map.end())
{
v.push_back(it);
it++;
}
sort(v.begin(), v.end(), com());
for (int i = 0; i < k; i++)
{
cout << v[i]->first << ":" << v[i]->second << " ";
}
}
k个数的小堆:
#include<queue>
typedef map<string, int>::iterator count_it;
class Greater
{
public:
bool operator()(const count_it& x, const count_it y)
{
//建k个数的小堆
if (x->second != y->second)
{
return x->second > y->second;
}
else
{ //次数相同,按asii比较,要想让asii小的放前面,要按大堆放,先把小的放后面,等会逆转就去前面了。
return x->first < y->first;
}
}
};
void testFriut1(const vector<string>& str,int k)
{
map<string, int> countMap;
//asii按升序排好,去重并统计出次数
for (auto& e : str)
{
countMap[e]++;
}
priority_queue<count_it, vector<count_it>, Greater> LitteHeap;
count_it it = countMap.begin();
while (k--)
{
LitteHeap.push(it);
it++;
}
while(it!=countMap.end())
{
if (it->second > LitteHeap.top()->second)
{
LitteHeap.pop();
LitteHeap.push(it);
}
it++;
}
vector<string> v;
while (!LitteHeap.empty())
{
v.push_back(LitteHeap.top()->first);
LitteHeap.pop();
}
reverse(v.begin(), v.end());
for (auto& e : v)
{
cout << e << " ";
}
}
map去重,asii码排降序。
mutimap按次数排序。
#include <functional>
//less默认都能用,greater就要包头文件
void testFriut2(const vector<string>& str, int k)
{
//asii码默认升序,且去重
map<string, int> count_map;
for (auto& e : str)
{
count_map[e]++;
}
//次数按降序
multimap<int,string,greater<int>> multimapCount;
//这可以用范围for插入,都差不多
count_it it = count_map.begin();
while (it != count_map.end())
{
multimapCount.insert(make_pair(it->second,it->first));
it++;
}
vector<string> v;
auto it1 = multimapCount.begin();
while (k--)
{
v.push_back(it1->second);
it1++;
}
for (auto& e : v)
{
cout << e << " ";
}
}
5.2 取前k个高频单词
由于要求是,当次数相同时,asii码小的在前面,所以不能用sort,因为他是快排,不稳定,相同次数,单词可能会改变顺序。
map去重,按asii码值排升序。
multimap按次数,排降序
class Solution {
public:
vector<string> topKFrequent(vector<string>& words, int k) {
map<string,int> countMap;
for(auto& e:words)
{
//没有K,插入K做first。有K,让second++
countMap[e]++;
}
//map中去除重复的单词,统计了次数,且按asii码值排升序
//让次数做k,次数可以重复
multimap<int,string,greater<int>> multimapCount;
for(auto& e:countMap)
{
multimapCount.insert(make_pair(e.second,e.first));
}
vector<string> v;
auto it=multimapCount.begin();
while(k--)
{
v.push_back(it->second);
it++;
}
return v;
}
};
取前k个建小堆
typedef map<string,int>::iterator count_it;
struct Greater{
public:
bool operator()(const count_it& x,const count_it& y)
{
if(x->second!=y->second)
//假如次数不同,那么就是小堆,堆顶就是第k个次数最多的,最后逆置。
return x->second > y->second;
else
//次数相同按大堆排,因为这样才能把asii值大的放在前面,逆置的时候就把asii小的就在前面了。
return x->first < y->first;
}
};
class Solution {
public:
vector<string> topKFrequent(vector<string>& words, int k) {
//asii值默认小于,即升序。
map<string,int> countMap;
for(auto& e:words)
{
countMap[e]++;
}
priority_queue<count_it,vector<count_it>,Greater> LittleHeap;
count_it it=countMap.begin();
while(k)
{
LittleHeap.push(it);
k--;
it++;
}
while(it!=countMap.end())
{
if(it->second>LittleHeap.top()->second)
{
LittleHeap.pop();
LittleHeap.push(it);
}
it++;
}
vector<string> ret;
while(!LittleHeap.empty())
{
ret.push_back(LittleHeap.top()->first);
LittleHeap.pop();
}
reverse(ret.begin(),ret.end());
return ret;
}
};
以上是关于STL容器_map与set的主要内容,如果未能解决你的问题,请参考以下文章
STL容器用法速查表:list,vector,stack,queue,deque,priority_queue,set,map
[C/C++]详解STL容器9-基于红黑树模拟实现map和set
[C/C++]详解STL容器9-基于红黑树模拟实现map和set
[C/C++]详解STL容器9-基于红黑树模拟实现map和set