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,支持了。

数据量小时:

  1. 排序,取出前k个.(但要注意排序不稳定)
  2. 大堆,取k次堆顶。
    数据量大时:
  3. 建k个数的小堆
  4. 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

[C/C++]详解STL容器9-基于红黑树模拟实现map和set

STL容器