【C++高阶(二)】熟悉STL中的map和set --了解KV模型和pair结构

简介: 【C++高阶(二)】熟悉STL中的map和set --了解KV模型和pair结构

1. 前言

在学习了二叉搜索树后,现在

就可以来学习map和set了,虽然

它们的底层是红黑树结构,但是红黑树

的本质也是一颗二叉搜索树!

本质重点:

本篇文章着重讲解map和set的
使用方法以及一些特性,以及讲解
muti为前缀的map/set和普通map/set
的区别,其中会学到一个重要的结构
pair,它会伴随我们很久


2. map和set介绍

set是key模型,本质是确定一个
元素在不在此容器中,也就是说
set中存储的是一个单一数据

map和set的区别就是,map中存储
的并不是一个单一数据,而是存储了
一个pair结构!(后面会讲解)

map的模板参数中有key和T
而set的模板参数只有T

set和map结构中遍历出来是

数据有序并且去重的!

map和set都只支持增删查,不支持改!


3. pair结构介绍

pair结构实际上是一个键值对

以下是对于键值对的介绍:

这个类的代码大概是这样的:

template <class T1, class T2>
struct pair
{
  T1 first;
  T2 second;
  pair(): first(T1()), second(T2())
  {}
  pair(const T1& a, const T2& b): first(a), second(b)
  {}
};

它实际上就是封装了两个可以是不同
类型的数,它可以用作中英字典,存储
pair<string,string>的结构,first存储中文
second存储对应的英文解释,非常好用!

map中存储的就是pair结构,所以
map也叫存储的KV模型,因为first和
second对应key和value

map的三种常见使用方法:

1. 方法一: 定义pair对象后插入

map<string,string> dict;
pair<string,string> kv1("排序","sort");
pair<string,string> kv2("左边","left");
dict.insert(kv1);
dict.insert(kv2);

2. 方法二: 使用匿名对象插入

map<string,string> dict;
dict.insert(pair<string,string>("排序","sort"));
dict.insert(pair<string,string>("左边","left"));

3. 方法三: 使用make_pair插入

map<string,string> dict;
dict.insert(make_pair("排序","sort"));
dict.insert(make_pair("左边","left"));

make_pair是最好用的方法!


4. set结构详解

先看set的第二个模板参数是less

是不是很熟悉?set默认情况下遍历

出来是升序,但是如果定义对象时显示

传参greater,就是降序!

set<int,greater<int>> s;

set的插入函数insert

插入我们需要关心三点:
一是插入可以不用写pos,直接插入
二是可以插入一段迭代器区间
三是插入的返回值也是一个pair结构
pair中存储了布尔类型和迭代器,分别
代表此次插入是否成功,若成功则返回
被插入元素迭代器的位置

set的查找和删除函数find,erase


5. map结构详解

set是没有支持方括号的

而map支持了,所以先讲解方括号的使用

map的方括号用法:

先看文档:

map的方括号设计的十分巧妙
它的参数的pair中的first,返回值
是pair中的second,并且它返回的是
引用,可以根据first修改second
它可以用来计数,请看如下demo代码:

统计水果的数量:

string arr[]={"苹果","西瓜","香蕉","苹果","西瓜","西瓜","西瓜","苹果"};
map<string,int> countmap;
for(auto str : arr)
{
  countmap[str]++;
}

请再看文档的详细信息:

也就是说,方括号自带插入功能,当
出现第一个"西瓜"时,会自动把"西瓜"
插入到map中,第二次遇见"西瓜"时,
会将西瓜的计数++变成2

map的删除和查找:

由于map的插入在前面已经讲解过了
这里只讲解删除和查找

erase和find传参只用传pair中的first

类型的参数,即可完成任务,并且find的

返回值和set的find是一样的,一个意思


6. multimap和multiset

map和set的遍历是有序并且去重的
也就是说里面没有相同的元素,但是
STL提供了multimap和multiset
它们允许存在相同的元素!

由于支持元素冗余
所以multimap不支持方括号了!

当我们插入相同的数据时,它也会保存


7. map和set实战演练

请看下面的力扣题目:

力扣692题

大家先思考一下对策吧

题目解析:

本题目不仅仅要找出前k个高频的单词
并且还要按照字典序来排序,也就是说,
要满足两个排序的条件:字符串出现的次数
和字符串在字典中的顺序!

想到要计数,当然是用map啦!

map<string,int> mpcount;
for(auto str : words)
  mpcount[str]++;

两个细节问题以及结论

  • map计数后,只需以map中的second
    作为基准来排序即可得到前k个高频
  • map本身的遍历就是有序的,并且此
    顺序刚好是字典序(以first排序的)

结论:只需使用一个排序算法,在不打乱
map原本排序的情况下,以map中的
second为基准排个序,即可得到我们
想要的答案,所以关键是要使用稳定
的排序算法!

库中稳定的排序算法:stable_sort

下面是所有的代码,不懂可以私信我

class Solution {
public:
    struct _compare
    {
        bool operator()(pair<string,int> p1,pair<string,int> p2)
        {
            return p1.second>p2.second;
        }
    };
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string,int> mpcount;
        for(auto str : words)
            mpcount[str]++;
        vector<pair<string,int>> tmp;
        auto it = mpcount.begin();
        while(it!=mpcount.end())
        {
            tmp.push_back(*it);
            it++;
        }
        _compare com;
        vector<string> ret;
        stable_sort(tmp.begin(),tmp.end(),com);
        for(int i=0;i<k;i++)
            ret.push_back(tmp[i].first);
        return ret;
    }
};

8. 总结

熟悉map和set的使用在平常做题

时会有大用处,虽然平时用的更多的

是unordered_map/set,但是它们的

使用方法基本一致,学到就是赚到!


🔎 下期预告:AVL树深度剖析 🔍


相关文章
|
3月前
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
96 2
|
3月前
|
存储 算法 C++
【c++丨STL】map/multimap的使用
本文详细介绍了STL关联式容器中的`map`和`multimap`的使用方法。`map`基于红黑树实现,内部元素按键自动升序排列,存储键值对,支持通过键访问或修改值;而`multimap`允许存在重复键。文章从构造函数、迭代器、容量接口、元素访问接口、增删操作到其他操作接口全面解析了`map`的功能,并通过实例演示了如何用`map`统计字符串数组中各元素的出现次数。最后对比了`map`与`set`的区别,强调了`map`在处理键值关系时的优势。
200 73
|
3月前
|
存储 算法 C++
【c++丨STL】set/multiset的使用
本文深入解析了STL中的`set`和`multiset`容器,二者均为关联式容器,底层基于红黑树实现。`set`支持唯一性元素存储并自动排序,适用于高效查找场景;`multiset`允许重复元素。两者均具备O(logN)的插入、删除与查找复杂度。文章详细介绍了构造函数、迭代器、容量接口、增删操作(如`insert`、`erase`)、查找统计(如`find`、`count`)及`multiset`特有的区间操作(如`lower_bound`、`upper_bound`、`equal_range`)。最后预告了`map`容器的学习,其作为键值对存储的关联式容器,同样基于红黑树,具有高效操作特性。
136 3
使用 entrySet 遍历 Map 类集合 KV
使用 entrySet 遍历 Map 类集合 KV
|
4月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
12天前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
|
12天前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
|
2月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
86 12
|
3月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
74 16
|
3月前
|
编译器 C++
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。