【C++】set和map

news2024/11/14 20:12:49

set和map

  • 1. 预备知识
  • 2. set
    • 2.1 set的概念
    • 2.2 set的常见接口
  • 3. multiset
  • 4. map
    • 4.1 map的概念
    • 4.2 map的常见接口
  • 5. multimap
  • 6. 练习

1. 预备知识

  1. set和map是关联式容器,里面存储的不是元素本身,存储的是<key,value>结构的键值对,比vector、list、deque这些底层为线性序列的序列式容器,在数据检索时效率更高。
  2. 关联性容器有两种不同的结构:树形结构和哈希结构。set和map的底层结构是树形结构。使用平衡搜索树(即红黑树)作为底层结构,容器中的元素是一个有序的序列
  3. 键值对一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。

2. set

2.1 set的概念

在这里插入图片描述

  1. set存储的是具有一定次序(升序和降序)的元素。
  2. set存储的元素被const修饰,不可更改。为什么?因为set的底层是用二叉搜索树实现的,改变了元素会影响次序。
  3. 用迭代器遍历set,可以得到有序序列。
  4. set中的元素是唯一的,因此使用set会去重。
  5. set看起来存放的只有key值,实际底层存放的是<value,value>键值对。

2.2 set的常见接口

  1. 构造

在这里插入图片描述

  1. 迭代器

在这里插入图片描述

void test1()
{
	//构造空set
	set<int> s1;
	s1.insert(3);
	s1.insert(2);
	s1.insert(4);
	s1.insert(5);
	s1.insert(1);
	auto it = s1.begin();
	//用迭代器遍历set,可以得到有序序列。
	while (it != s1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	//用迭代器构造set
	set<int>s2(s1.begin(), s1.end());
	for (auto e : s2)
	{
		cout << e << " ";
	}
	cout << endl;
	//拷贝构造
	set<int>s3(s2);
	for (auto e : s3)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

  1. 容量

在这里插入图片描述

  1. 增加

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

在这里插入图片描述

  1. 删除

在这里插入图片描述
若删除的参数是键值,键值存在就删除,不存在也不会报错。若删除的参数是迭代器,迭代器是end(),就会报错。

void test2()
{
	set<int> s1;
	s1.insert(3);
	s1.insert(2);
	s1.insert(4);
	s1.insert(5);
	s1.insert(1);
	auto it = s1.find(6);
	s1.erase(6);//✔
	//s1.erase(it);//❌
}
  1. 交换

在这里插入图片描述

  1. 清理

在这里插入图片描述

void test3()
{
	set<int> s1;
	s1.insert(3);
	s1.insert(2);
	s1.insert(4);
	s1.insert(5);
	s1.insert(1);
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	set<int>s2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	//交换s1和s2
	s1.swap(s2);
	cout << "s1的元素:" ;
	for (auto e : s1)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "s2的元素:";
	for (auto e : s2)
	{
		cout << e << " ";
	}
	cout << endl;
	//清理s1
	s1.clear();
	cout <<"s1大小为:" << s1.size() << endl;
	cout << "s1是否为空:" << s1.empty() << endl;
}

在这里插入图片描述

  1. 查找

在这里插入图片描述
(1)查找键值为val的元素,找到了返回该元素在set中的迭代器,找不到返回end()的迭代器。
(2)库里的find和set的find有区别。库里的find是暴力查找,时间复杂度是O(N),set的find是大于查找元素就往左找,小于查找元素就往右找。

  1. 计数

在这里插入图片描述
返回set中与val相等的元素个数。由于set会去重 ,元素都是唯一,所以count只能返回0或者1,所以count也可以用来判断元素是否在set中。

  1. 获得迭代器区间

在这里插入图片描述
(1)lower_bound找到>=参数的元素的迭代器,upper_bound找到>参数的最小元素的迭代器。配合使用,可以获得左闭右开的区间。
(2)若两个函数的参数相同,则区间内的元素都是相同的,但set的元素是唯一的,所以这两函数用处不大,可以用于multiset。
(3)若参数在set中找不到,则返回set的end()的迭代器。

void test4()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	set<int>s1(arr, arr + sizeof(arr) / sizeof(arr[0]));
	auto begin = s1.lower_bound(5);
	auto end = s1.upper_bound(10);
	while (begin != end)
	{
		cout << *begin << " ";
		++begin;
	}
	cout << endl;
}

在这里插入图片描述

  1. 获得相同元素的迭代器区间

在这里插入图片描述
(1)在set中查找相同元素的区间,返回左闭右开的区间。
(2)返回键值对,键值对的第一个迭代器是set中val的位置,第二个迭代器是set中大于val的最小元素的位置。
(3)对于set来说,用处不大,因为set不能有重复的元素,但对multiset就有用处。
(4)若参数不存在于set,返回类似于[参数,参数)这样不存在的区间。

void test5()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	set<int>s1(arr, arr + sizeof(arr) / sizeof(arr[0]));
	auto ret = s1.equal_range(5);
	auto itlow = ret.first;//找到5
	auto itupper = ret.second;//找到比5大的最小元素
	while (itlow != itupper)
	{
		cout << *itlow << " ";
		++itlow;
	}
	cout << endl;
}

在这里插入图片描述

3. multiset

  1. multiset是在set的基础上,加上可以插入重复元素的功能。multiset依旧是按照一定次序(升序和降序)存储元素,通过迭代器遍历元素可以得到有序序列。multiset的元素可以重复,不能修改。
  2. equal_range和count对于multiset用处更大,更有意义。
void test6()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 ,1,2,3,4,5,6,7,8,9 };
	multiset<int> ms(arr,arr+sizeof(arr)/sizeof(arr[0]));
	cout << "multiset的元素:";
	for (auto& e : ms)
	{
		cout << e << " ";
	}
	cout << endl;

	cout<<"9出现的次数:"<<ms.count(9)<<endl;

	auto range = ms.equal_range(5);
	auto low_range = range.first;
	auto upper_range = range.second;
	while (low_range != upper_range)
	{
		cout << *low_range << " ";
		++low_range;
	}
	cout << endl;
}

在这里插入图片描述


4. map

4.1 map的概念

在这里插入图片描述

  1. map也是关联式容器,存储的是具有一定次序(升序和降序)的元素。
  2. map中存放的是<key,value>键值对,key代表键值,value表示与key对应的信息。map中元素的次序就是根据key进行排序的,因此key不能修改,且key在map中唯一,value则不一定唯一。
//键值对的定义  
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)
	{}
};
  1. map通过迭代器遍历元素,可以得到有序序列。
  2. 特点:map支持[]访问,通过[key]可以找到对应的value。operator[]中实际进行插入查找。

4.2 map的常见接口

  1. 构造

在这里插入图片描述

  1. 迭代器

在这里插入图片描述
set的迭代器都是const迭代器,因此它的key不能修改,map的迭代器和const迭代器分明,key不可修改,value可以修改。
在这里插入图片描述
在这里插入图片描述

//例子
void test()
{
	map<string,string> dict;//词典
	//插入键值对
	dict.insert({"insert", "插入"});
	dict.insert({"erase", "删除"});
	dict.insert({"size", "大小"});
	dict.insert({"find","查找"});

	//为什么要将key和value封装?因为C++不支持返回两个值,但支持返回一个结构体
	auto it = dict.begin();
	while (it != dict.end())
	{
		//cout << *it << " ";//❌,pair不支持流插入
		cout << it->first << ":" << it->second << endl;
		++it;
	}
	cout << endl;
	//范围for
	for (auto& e : dict)
	{
		cout << e.first << ":" << e.second << endl;
	}
}

在这里插入图片描述

  1. 插入

在这里插入图片描述
(1)key与value通过成员类型value_type绑定在一起,为其取别名称为pair:typedef pair<const key, T> value_type;。
(2)不同于set,map插入的是键值对,不是单独的value。返回的也是键值对,插入成功,iterator是新插入节点的位置,bool是true;发现map已有相同的key,插入失败,iterator是map中key的位置,bool是false。
(3)如何插入键值对

//map
void test7()
{
	map<string,string> dict;//词典
	//法1
	//pair也是一个类模板
	pair<string, string>word1("insert", "插入");
	dict.insert(word1);
	//法2
	//先调用构造,再调用拷贝构造,编译器会优化为构造
	dict.insert(pair<string, string>("erase", "删除"));
	//法3
	//make_pair是模板函数,可以构造键值对
	dict.insert(make_pair("size", "大小"));
	//法4
	//C++11支持多参数的构造函数隐式类型转换,C++98支持单参数的构造函数隐式类型转换
	dict.insert({ "find","查找" });
	//等价于pair<string,string> word2 = {"find","查找"},然后dict.insert(word2);
}

在这里插入图片描述
最常用的是法3和法4。

  1. []下标访问
//例子:统计次数
//常规做法
void test9()
{
	string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
	map<string, int> countMap;
	for (auto e : arr)
	{
		auto it = countMap.find(e);
		if (it == countMap.end())
		{
			countMap.insert(make_pair(e, 1));
		}
		else
		{
			it->second++;
		}
	}
	for (const auto& kv : countMap)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
}
//优化
void test9()
{
	// 统计次数
	string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
	map<string, int> countMap;
	for (auto e : arr)
	{
		countMap[e]++;
	}
	for (const auto& kv : countMap)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
}

把key放入[]中,就可以得到相应的value,可问题是还没插入怎么++?[]下标访问,先试着插入key,如果key已经存在,则返回key对应的value,此时就可以对value操作。如果key不存在,先插入key,再返回对应的value。

//底层类似这样定义
T& operator[](cosnt K& key)
{
	pair<iterator,bool>ret = insert(make_pair(key,T()));//因为value可能是其他类型,所以不能单纯用int。
	//(1)key已在map里面,返回pair<map中key所在节点的位置,false>
	//(2)key不在map里面,返回pair<新插入key所在节点的位置,true>
	return ret->first->second;
}

练习
有效的括号

class Solution {
public:
    //将左括号入栈,遇到右括号出栈,判断是否相同,不相等就return false,
    bool isValid(string s) {
        stack<char> st;
        map<char,char> m;
        m['('] = ')';
        m['['] = ']';
        m['{'] = '}';
        for(auto e:s)
        {
            //如果e等于左括号,返回1,就入栈
            if(m.count(e))
            {
                st.push(e);
            }
            //遇到右括号,就出栈,判断是否相等
            else
            {
                if(st.empty())
                {
                   return false; 
                }
                char tmp = st.top();
                st.pop();
                if(m[tmp]!=e)
                {
                    return false;
                }
            }
        }
        if(st.empty())
        {
            return true;
        }
        return false;
    }
};

5. multimap

multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以重复的。因此multimap不支持[]下标访问。


6. 练习

  1. 两个数组的交集
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        //先将两个数组放进set中,可以帮助去重
        //法1:遍历一个set中所有元素,判断在不在另一个set中,在就加到交集中
        //法2:从头开始,比较两个set中的元素,相等就是加到交集,然后同时++,小的就指向下一个元素,继续比较
        set<int>s1(nums1.begin(),nums1.end());
        set<int>s2(nums2.begin(),nums2.end());

        vector<int>v;//存放交集
        auto it1 = s1.begin();
        auto it2 = s2.begin();
        while(it1!=s1.end()&&it2!=s2.end())
        if(*it1==*it2)
        {
            v.push_back(*it1);
            ++it1;
            ++it2;
        }
        else if(*it1>*it2)
        {
            ++it2;
        }
        else
        {
            ++it1;
        }
        return v;
    }
};
  1. 前k个高频单词
class Solution {
public:
    struct Greater
    {
        bool operator()(const pair<string,int>& p1,const pair<string,int>&p2)
        {
            return p1.second>p2.second;
        }
    };
    vector<string> topKFrequent(vector<string>& words, int k) {
        //法一:先将words的单词map中,可以起到去重和计数的作用
        map<string,int> m;
        for(auto& e:words)
        {
            m[e]++;
        }
        //此时map已经按照字典序排好了
        //再按照单词出现频率排序,使用sort进行排序,但是sort不能对map排序,因为map的迭代器是双向的,双向迭代器不能使用随机迭代器的函数。所以将map的键值对加到vector中,就可以用sort。
        vector<pair<string,int>> v(m.begin(),m.end());
        //但是sort底层是快排,快排不稳定,排序结束后,出现频率相同的单词的字典序可能被破坏,所以得用另一个函数stable_sort,stable_sort不会破坏字典序。
        //又因为是排降序,得自己写仿函数
        stable_sort(v.begin(),v.end(),Greater());
        //排序后需要将前K个string取出,放入vector
        vector<string> ret;
        for(int i = 0;i<k;i++)
        {
            ret.push_back(v[i].first);
        }
        return ret;
    }
};
class Solution {
public:
    //法2:如果偏要用sort排序,怎么办?修改仿函数,在频率相同的情况下不改变字典序。
    struct Greater
    {
        bool operator()(const pair<string,int>& p1,const pair<string,int>&p2)
        {
            return (p1.second>p2.second) ||(p1.second==p2.second&&p1.first<p2.first);
        }
    };
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string,int> m;
        for(auto& e:words)
        {
            m[e]++;
        }
        vector<pair<string,int>> v(m.begin(),m.end());
        sort(v.begin(),v.end(),Greater());
        vector<string> ret;
        for(int i = 0;i<k;i++)
        {
            ret.push_back(v[i].first);
        }
        return ret;
    }
};
class Solution {
public:
    //法3:用map按字典序排序,用multimap再按频率排序,multimap排序是稳定的,不会破坏字典序。
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string,int> m;
        for(auto& e:words)
        {
            m[e]++;
        }
        
        multimap<int,string,greater<int>>mm;
        for(auto&e:m)
        {
            mm.insert(make_pair(e.second,e.first));
        }
        
        vector<string>ret;
        auto it = mm.begin();
        while(k--)
        {
            ret.push_back(it->second);
            ++it;
        }
        return ret;
    }   
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/958852.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python入门教程 | Python3 字符串

字符串是 Python 中最常用的数据类型。我们可以使用引号( ’ 或 " )来创建字符串。 创建字符串很简单&#xff0c;只要为变量分配一个值即可。例如&#xff1a; var1 Hello World! var2 "Tarzan"Python 访问字符串中的值 Python 不支持单字符类型&#xff…

J. Med. Chem 2022|TocoDecoy+: 针对机器学习打分函数训练和测试的无隐藏偏差的数据集构建新方法

原文标题&#xff1a;TocoDecoy: A New Approach to Design Unbiased Datasets for Training and Benchmarking Machine-Learning Scoring Functions 论文链接&#xff1a;https://pubs.acs.org/doi/10.1021/acs.jmedchem.2c00460 论文代码&#xff1a;GitHub - 5AGE-zhang/T…

苹果手机安装Loanmate步骤

1.电脑端下载爱思助手桌面软件并安装 https://www.i4.cn/ 2.苹果手机连接电脑&#xff0c;出现以下成功连接界面 3.切换到应用游戏选项 4.把Loanmate.ipa 包拖到3 界面 5.等待手机安装成功

智能电话机器人的出现,能够解决哪些问题?

经济的繁荣与高速的发展&#xff0c;使得电销这个方式快速地融合在房地产与金融投资等大部分行业上。在电销人员与客户的沟通上&#xff0c;难免会出现很多问题&#xff0c;毕竟所面对的客户都是各行各业&#xff0c;他们有着不同的经历和身份。 对于时常需要处理客户投诉、安…

linux上vscode中.cpp文件中引入头文件.hpp时报错:找不到头文件(启用错误钵形曲线)

当在.cpp文件中引入系统给定的头文件时&#xff1a;#include < iostream > 或者引入自定义的头文件 &#xff1a;#include <success.hpp> 报错&#xff1a;找不到相应的头文件&#xff0c;即在引入头文件的改行底下标出红波浪线 解决方法为&#xff1a; &#…

【前端】CSS-flexbox弹性盒模型布局

目录 一、前言二、flexbox简介三、属性1、容器属性2、容器成员属性 四、容器属性的作用1、flex-direction①、定义②、语句1&#xff09;、属性值 ③、代码示例1&#xff09;、flex-direction: row2&#xff09;、flex-direction: row-reverse3&#xff09;、flex-direction: c…

篇篇10万赞,3D原创动漫视频教程

本期是赤辰第25期AI项目教程&#xff0c;底部准备了8月粉丝福利&#xff0c;可以免费领取。 赤辰AI实操记&#xff0c;专注于AI改造项目与生产方法分享&#xff1a;AI高效变现案例拆解、AI生产力工具使用技巧、改善工作流、AI自动化运营技术等干货分享 今天给大家拆解一个近期…

探讨DPU驱动的高效AI大模型算力底座 中科驭数亮相中国IDC产业(长三角)年度大典

8月31日&#xff0c;以“AI造物 算引变革”为主题的第十八届中国IDC产业&#xff08;长三角&#xff09;年度大典今日在上海隆重召开。大会由中国IDC产业年度大典组委会、上海市通信学会“算力浦江”专委会、中国通信工业协会数据中心委员会联合主办。中科驭数产品运营部副总经…

go学习part21 Redis

300_尚硅谷_Redis的基本介绍和原理示意_哔哩哔哩_bilibili Redis 命令 | 菜鸟教程 (runoob.com) 1.基本介绍 2.基本操作 Redis的基本使用: 说明:Redis安装好后&#xff0c;默认有16个数据库&#xff0c;初始默认使用0号库,编号是0...15 1.添加key-val [set] 2.查看当前redi…

打造全球研发中心城市,湖南湘江新区的挑战与机遇

作者 | 魏启扬 来源 | 洞见新研社 图源&#xff1a;长沙晚报 十年前&#xff0c;中国高铁还在为是选择德国技术&#xff0c;还是日本方案左右为难&#xff0c;如今全面自主化的“复兴号”已有超千组车组投入运营&#xff0c;我们随时可以来一场说走就走的旅行。 十年前&…

【原创】H3C交换机链路聚合配置

图示 中间两个交换机&#xff0c;使用两根网线直连&#xff0c;这样本来是10G级联&#xff0c;变成了20G级联。 在默认情况下&#xff0c;这两根线在STP协议下&#xff0c;只有一路是通的&#xff0c;另一路处于备用状态。如果要将这两路都设置为级联&#xff0c;那么还需要…

MySQL高阶语句之常用查询

目录 常用查询 按关键字排序 区间判断及查询不重复记录 对结果进行分组 限制结果条目 设置别名 通配符 子查询 常用查询 &#xff08;增、删、改、查&#xff09; 对 MySQL 数据库的查询&#xff0c;除了基本的查询外&#xff0c;有时候需要对查询的结果集进行处理。 …

JasperReport定义变量后打印PDF变量为null以及整个pdf文件为空白

问题1: JasperReport打印出来的整个pdf文件为空白文件&#xff1b; 问题2&#xff1a;JasperReport定义变量后打印PDF变量为null&#xff1b; 问题1原因是因为缺少数据源JRDataSource JasperFillManager.fillReport(jasperReport, params,new JREmptyDataSource());如果你打印…

虹科案例 | 缆索挖掘机维护—小传感器,大作用!

一、 应用背景 缆索挖掘机 缆索挖掘机的特点是具有坚固的部件&#xff0c;如上部结构、回转环和底盘。底盘是用于移动挖掘机的下部机械部件&#xff0c;根据尺寸和型号的不同&#xff0c;由轮子或履带引导&#xff0c;并承载可转动的上部车厢。回转环连接上部和下部机器部件&am…

LTE ATTACH流程、PDN流程、PGW地址分配介绍

1、S-GW\P-GW选择 MME根据S-GW和P-GW的拓扑信息进行S-GW/P-GW的选择&#xff0c;在S-GW的候选序列和P-GW的候选序列中比较&#xff0c;寻找是否有合一的S-GW/P-GW&#xff0c;并且根据S-GW的优先级和权重信息进行排序&#xff0c;得到S-GW/P-GW的候选组。 2、SGW>PGW连接 PD…

简单了解网络传输介质

目录 一、同轴电缆 二、双绞线 三、光纤 四、串口电缆 一、同轴电缆 10BASE前面的数字表示传输带宽为10M&#xff0c;由于带宽较低、现在已不再使用。 50Ω同轴电缆主要用来传送基带数字信号&#xff0c;因此也被称作为基带同轴电缆&#xff0c;在局域网中得到了广泛的应用…

在VScode中如何将界面语言设置为中文

VSCode安装后的默认界面是只有英文的&#xff0c;如果想用中文界面&#xff0c;那么就需要安装对应的插件&#xff0c;vscode插件可以从扩展中心去搜索并安装。 安装vscode后打开vscode&#xff0c;点击左侧的扩展按钮。 在搜索框中输入chinese&#xff0c;弹出chinese&#x…

卫星通话发展

终端演进 华为Mate 50可以通过北斗卫星给个人定向发送文字、位置、轨迹图等信息&#xff0c;但内容会被审核&#xff0c;只有跟救援相关的信息才能被发送&#xff0c;而且收不到回复。 华为P60系列上&#xff0c;卫星通信功能被升级为双向&#xff0c;即支持发送和接收短信。…

校园广播解决方案,公共广播解决方案

校园广播解决方案&#xff0c;公共广播解决方案 网络IP对讲系统的优势 a) 更强的功能 纯数字对讲系统&#xff0c;涵盖了传统对讲系统和校园广播系统的所有功能。并充分利用了校园网络的资源&#xff0c;可随时随地获取网络上的音频资源。由于每个终端有独立的IP地址…

第64步 深度学习图像识别:多分类建模误判病例分析(Pytorch)

基于WIN10的64位系统演示 一、写在前面 上期我们基于TensorFlow环境介绍了多分类建模的误判病例分析。 本期以健康组、肺结核组、COVID-19组、细菌性&#xff08;病毒性&#xff09;肺炎组为数据集&#xff0c;基于Pytorch环境&#xff0c;构建SqueezeNet多分类模型&#xf…