欢迎光临
我们一直在努力

深入理解C++系列(14)——map和set的使用

⭐️博主: 此生决int-@CSDN博客

 速胜派就是最大的投降派!!!

         🔥热门专栏🔥

      深入理解 C++ 系列 | 算法系列

      快速复习系列 | Java 速通系列


文章目录

    • 上期回顾
  • map和set的使用
    • 1. 序列式容器和关联式容器⭐️
    • 2. set系列的使用
      • 2.1 set和multiset参考文档
      • 2.2 set类的介绍
      • 2.3 set的构造和迭代器⭐️
        • 1,构造
        • 2,迭代器
      • 2.4 set的增删查⭐️⭐️⭐️
        • 1,插入insert
        • 2,删除eraser
        • 3,clear清空所有元素
        • 4,查找find
        • 5,count统计元素个数
        • 6,范围查找——lower_bound和upper_bound
        • 运用————找到这样一个区间:[30, 60]
      • 2.7 multiset和set的差异
    • 3,set相关的简单算法题
      • 两个数组的交集⭐⭐
        • 题目链接
        • 题目描述
        • 解题思路
        • 解题代码
        • 解题思路2
        • 解题代码
      • 环形链表 II⭐⭐⭐
        • 题目链接
        • 题目描述
        • 解题思路
        • 解题代码
        • 大神解题代码
    • 3. map系列的使用
      • 3.1 map和multimap参考文档
      • 3.2 map类的介绍
      • 3.3 pair类型介绍(以后常用!!)⭐️⭐️
      • 3.4 map的构造
      • 3.5 map的增删查⭐️⭐️
        • 1,插入insert
        • 2,删除erase
        • 3,clear清空所有元素
        • 4,查找find
        • 5,count统计元素个数
        • 6,operator[]访问和修改元素⭐️⭐️⭐️
      • 3.6 map的数据修改
      • operator[]内部实现详解:
      • 3.9 multimap和map的差异
    • map相关算法题⭐️⭐️⭐️⭐️
      • 1,复制带随机指针的链表⭐⭐⭐⭐
        • 题目链接
        • 题目描述
        • 解题思路
        • 我的解题代码
        • 优化版,思路一样
        • 大神解题代码(深拷贝,直接原地复制数组)
      • 2,前 K 个高频单词⭐⭐
        • 题目链接
        • 题目描述
        • 解题思路
        • 解题代码
        • 大神解题代码
    • 下期预告
    • AVL树
    • 结语

上期回顾

上一篇我们主要学习了 二叉搜索树,了解了二叉搜索树的相关特性,手动模拟实现了key版和key/value版本的普通的二叉搜索树,那么今天,我们就来用一用STL里面封装好了并且优化了的二叉搜索树——set和map系列!让我们开始今天的学习之旅吧!

map和set的使用

1. 序列式容器和关联式容器⭐️

简单来说: 序列式容器:逻辑为线性结构,两个位置的值之间没有关系,交换一下,它依旧是序列式容器。,例如,string,vector,list等等 关联式容器:逻辑非线性结构,两个位置的值之间有关系,交换一下,它的存储结构就被破坏了。,例如,二叉搜索树(map/set)和unordered_map/unordered_set 系列。

在这里插入图片描述

2. set系列的使用

2.1 set和multiset参考文档

set和multiset参考文档

2.2 set类的介绍

总结: 1,set 2,set<T,greater>, 第一个参数为键值,,默认小根,如果要大根,第二个参数传仿函数,set<T,Greater> 3,增删查效率是 O(logN),不支持改!!,

template < class T, // set::key_type/value_type
class Compare = less<T>, // set::key_compare/value_compare
class Alloc = allocator<T> // set::allocator_type
> class set;

2.3 set的构造和迭代器⭐️

1,构造

set<int> s1;//默认构造
set<int> s2(s1);//拷贝构造
set<int> s3(s1.begin(),s1.end());//迭代器区间构造
set<int> s4={1,5,3,7};//初始化列表构造

2,迭代器

1,set是双向迭代器,所以有begin()/rbegin(),end()/rend(); 2,set的迭代器不支持修改数据 3,范围for遍历采用中序遍历,遍历是有序的

2.4 set的增删查⭐️⭐️⭐️

1,插入insert

示例代码如下:

//set不允许插入重复元素
//直接插入value
s.insert(10);
//迭代器插入
set<int> s1={1,2,3,5,6};
s.insert(s1.begin(),s1.end());
// 插入一段 initializer_list 列表值,已经存在的值插入失败
s.insert({ 2,8,3,9 });

2,删除eraser

1,erase按照key删除

s.erase(10);

2,erase按照迭代器删除

auto it=s.begin();
s.erase(it);

3,erase按照迭代器区间删除

s.erase(s.begin(),s.end());

注意:删除后迭代器失效!!!

3,clear清空所有元素
4,查找find

1,find查找元素

auto it=s.find(key);

返回值 1,找到:返回元素迭代器 2,未找到:返回end() 这里注意,库里面也用一个find,但是效率太慢了(O(N)),优先使用set的find函数接口

// 算法库的查找 O(N)
auto pos1 = find(s.begin(), s.end(), x);
// set 自身实现的查找 O(logN)
auto pos2 = s.find(x);
//find和eraser的配合
// 直接查找再利用迭代器删除 x
cin >> x;
auto pos = s.find(x);
if (pos != s.end())
{
s.erase(pos);
}
else
{
cout << x << "不存在!" << endl;
}

相似的:

  • 二叉搜索树类的STL的find都用自己的
  • list有list.sort和list.reverse,所以,list可以用自己的sort和reverse
  • 所有容器的swap,都优先使用容器.swap,而不是库里的swap
  • 优先使用库里的情况:

  • 1,vector/string没有reverse,所以只能用库里的,list用自己的
  • 总之,容器自己实现了肯定优先使用容器的

    5,count统计元素个数

    s.count(key);

    因为set不允许重复:所以只能返回0(不存在)和1(存在)

    主要运用:查看是否存在某个key⭐️⭐️⭐️

    // 利用 count 间接实现快速查找
    cin >> x;
    if (s.count(x))
    {
    cout << x << "在!" << endl;
    }
    else
    {
    cout << x << "不存在!" << endl;
    }

    6,范围查找——lower_bound和upper_bound

    1,lower_bound

    auto it=s.lower_bound(key);//返回第一个大于等于key的元素
    auto it=s.upper_bound(key);//返回第一个大于key的元素

    运用————找到这样一个区间:[30, 60]

    std::set<int> myset;
    for (int i = 1; i < 10; i++)
    myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90
    // 实现查找到的 [itlow, itup) 包含 [30, 60] 区间
    // 返回 >= 30的第一个迭代器log(N);
    auto itlow = myset.lower_bound(30);
    // 返回 > 60的第一个迭代器log(N)
    auto itup = myset.upper_bound(60);
    // 删除这段区间的值
    myset.erase(itlow, itup);

    2.7 multiset和set的差异

  • 1,头文件同一个
  • 2,multiset 和 set 的使用基本完全类似,主要区别点在于 multiset 支持值冗余,即相比 set 不同的是,multiset 是排序,但是不去重
  • 3, insert/find/count/erase 都围绕着支持值冗余有所差异,例如:
  • multiset: find,找中序遍历的第一个key eraser 为key的所有全部删掉 cont 有几个key返回几

    3,set相关的简单算法题

    两个数组的交集⭐⭐

    题目链接

    两个数组的交集


    题目描述

    给定两个数组 nums1 和 nums2,返回它们的交集。结果中的每个元素必须是唯一的,返回结果可以按照任意顺序。 在这里插入图片描述


    解题思路

    利用 set 的去重和查找特性。先将第一个数组中的元素存入 set,然后遍历第二个数组,如果元素存在于第一个 set 中,则加入结果 set,最后转换成 vector 返回。


    解题代码

    class Solution {
    public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {

    // 存储 nums1 中的元素,自动去重
    set<int> s1;

    // 存储最终交集,保证结果唯一
    set<int> ret;

    // 将 nums1 中所有元素加入集合
    for (auto it : nums1)
    s1.insert(it);

    // 判断 nums2 中的元素是否存在于 nums1 集合中
    for (auto it : nums2)
    {
    if (s1.count(it))
    ret.insert(it);
    }

    vector<int> rret;

    // set 转换为 vector
    for (auto it : ret)
    rret.push_back(it);

    return rret;
    }
    };


    解题思路2

    利用 set 自动去重并排序的特点。

    先分别将两个数组转换成 set,然后使用两个迭代器同时遍历两个集合:

    • 如果两个元素相等,说明找到交集,加入结果。
    • 如果当前 nums1 的元素较小,则移动 nums1 的迭代器。
    • 否则移动 nums2 的迭代器。

    类似归并排序中的双指针思想。


    解题代码

    class Solution {
    public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {

    set<int> s1;
    set<int> s2;

    vector<int> ret;

    // 保存 nums1 中的元素,自动去重并排序
    for (int x : nums1)
    {
    s1.insert(x);
    }

    // 保存 nums2 中的元素,自动去重并排序
    for (int x : nums2)
    {
    s2.insert(x);
    }

    // 两个 set 同时遍历
    auto cur1 = s1.begin();
    auto cur2 = s2.begin();

    while (cur1 != s1.end() && cur2 != s2.end())
    {
    if (*cur1 == *cur2)
    {
    // 找到公共元素
    ret.push_back(*cur1);

    cur1++;
    cur2++;
    }
    else
    {
    // 小的元素不可能再匹配,移动较小的一方
    if ((*cur1) < (*cur2))
    cur1++;
    else
    cur2++;
    }
    }

    return ret;
    }
    };

    环形链表 II⭐⭐⭐

    题目链接

    环形链表 II


    题目描述

    给定一个链表的头节点 head,判断链表是否存在环。如果存在环,返回环的入口节点;如果不存在环,返回 nullptr。 在这里插入图片描述


    解题思路

    使用 set 记录已经访问过的节点地址。

    遍历链表时,如果当前节点已经存在于 set 中,说明之前访问过该节点,即链表形成环,当前节点就是环的入口。

    如果遍历结束仍未重复,则说明不存在环。


    解题代码

    /**
    * Definition for singly-linked list.
    * struct ListNode {
    * int val;
    * ListNode *next;
    * ListNode(int x) : val(0), next(NULL) {}
    * };
    */

    class Solution {
    typedef struct ListNode listnode;

    public:

    typedef struct ListNode ListNode;

    ListNode *detectCycle(ListNode *head) {

    // 保存已经访问过的节点地址
    set<ListNode*> s1;

    auto it = head;

    while (it)
    {
    // 如果节点已经访问过,说明进入环
    if (s1.count(it))
    return it;

    // 记录当前节点
    s1.insert(it);

    // 继续向后遍历
    it = it->next;
    }

    // 遍历结束,没有环
    return it;
    }
    };


    没懂?看看大神的解题代码!!

    大神解题代码

    /**
    * Definition for singly-linked list.
    * struct ListNode {
    * int val;
    * ListNode *next;
    * ListNode(int x) : val(0), next(NULL) {}
    * };
    */

    class Solution {
    public:

    ListNode *detectCycle(ListNode *head) {

    if (head == nullptr)
    return nullptr;

    ListNode* slow = head;
    ListNode* fast = head;

    // 快慢指针判断是否有环
    while (fast && fast->next)
    {
    slow = slow->next;
    fast = fast->next->next;

    if (slow == fast)
    {
    // 找环入口
    ListNode* cur = head;

    while (cur != slow)
    {
    cur = cur->next;
    slow = slow->next;
    }

    return cur;
    }
    }

    return nullptr;
    }
    };

    3. map系列的使用

    3.1 map和multimap参考文档

    map和multimap参考文档

    3.2 map类的介绍

    与set几乎一样,只是第一个参数变成了pair类型 map<pair<T1,T2>> mp; mp<pair<T1,T2>,greater>mp; 第一个参数是一个pair类型,第二个是仿函数

    3.3 pair类型介绍(以后常用!!)⭐️⭐️

    pair本质上就是:用一个结构体把两个值封装起来了,map里面的pair如下:

    typedef pair<const Key, T> value_type;//注意,mp里面的第一个参数是const修饰的,不能修改哦,普通的pair是可以修改的

    普通pair的底层实现

    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)
    {}

    template<class U, class V>
    pair(const pair<U, V>& pr)
    : first(pr.first), second(pr.second)
    {}
    };

    template <class T1, class T2>
    inline pair<T1, T2> make_pair(T1 x, T2 y)
    {
    return (pair<T1, T2>(x, y));
    }

    这里库提供了一个make_pair函数,

    pair<string string> p=make_pair("ac","bada");
    //同时,C++11支持隐式类型转换,比如,我们后面写insert的时候就可以:
    // C++11
    dict.insert({ "auto", "自动的" });

    3.4 map的构造

    map<string, string> dict;//默认构造
    pair<string, string> kv1("first", "第一个");
    map<string ,string> m2={kv1};//使用pair插入初始化
    //map<string string> m1(kv1);没有这种构造哦
    map<string, string> dict = { {"left", "左边"}, {"right", "右边"}, {"insert", "插入"},{ "string", "字符串" } };//使用初始化列表构造
    map<string,string> dict2(dict1.begin(),dict1.end());//迭代器构造
    map<string,string> dict2(m1);//拷贝构造

    map的迭代器等与set一致 但map迭代器访问与set不同:因为it是pair类型,pair类型没有重载operator<<,要用->first来访问

    auto it=m.find(1);

    cout<<it->first<<endl;//key
    cout<<it->second<<endl;//value

    3.5 map的增删查⭐️⭐️

    1,插入insert

    map<int,string> m;
    //插入pair
    m.insert(pair<int,string>(1,"张三"));
    //C++11支持隐式类型转换,所以支持这种写法
    m.insert({2,"李四"});

    注意; 1,map不允许插入重复key 2,key会自动排序 3,value可以重复

    2,插入一段迭代器区间

    map<int,string> m1=
    {
    {1,"a"},
    {2,"b"},
    {3,"c"}
    };
    map<int,string> m2;
    m2.insert(m1.begin(),m1.end());

    3,插入initializer_list列表值

    m.insert({
    {4,"d"},
    {5,"e"}
    });

    注意: 如果key已经存在,插入失败,不会覆盖原来的value


    2,删除erase

    1,erase按照key删除

    m.erase(1);
    0 //删除失败,key不存在
    1 //删除成功

    2,erase按照迭代器删除

    auto it=m.begin();

    m.erase(it);

    3,erase按照迭代器区间删除

    m.erase(m.begin(),m.end());

    注意: 删除后迭代器失效!!!


    3,clear清空所有元素
    4,查找find

    1,find查找元素

    auto it=m.find(key);

    1,找到:返回键值对的迭代器 2,未找到:返回end()

    5,count统计元素个数

    m.count(key);

    因为map不允许重复key:所以只能返回: 1,存在:1 2,不存在:0

    主要运用:查看某个key是否存在

    int x;
    cin>>x;
    if(m.count(x))
    {
    cout<<x<<"存在!"<<endl;
    }
    else
    {
    cout<<x<<"不存在!"<<endl;
    }

    注意: map也可以使用count实现快速查找是否存在,但是推荐find,因为find可以直接获得对应的value。


    6,operator[]访问和修改元素⭐️⭐️⭐️

    operator[]访问和修改元素是map最常用接口之一

    例如:

    map<int,string> m;

    m[1]="张三";

    cout<<m[1];

    注意: 1,根据key访问value 2,不存在时会插入新的键值对 3,如果只是判断key是否存在,不推荐使用:operator[],因为会导致插入。推荐:find


    3.6 map的数据修改

    前面我提到 map 支持修改 pair的第二个 数据,不支持修改 key 数据,修改关键字数据会破坏底层搜索树的结构。

    两种修改方法: 1,通过迭代器修改 2,operator[]修改,注意,如果不存在这个key,它会直接插入!!

    operator[]内部实现详解:

    首先我们要知道,operator[]既可以增加key,也可以查找,所以,operator[]内部是用insert来实现的

    // 官方文档中对 insert 返回值的说明
    // The single element versions (1) return a pair, with its member pair::first
    // set to an iterator pointing to either the newly inserted element or to the
    // element with an equivalent key in the map. The pair::second element in the pair
    // is set to true if a new element was inserted or false if an equivalent key
    // already existed.

    简单来讲就是 insert 插入一个 pair<key, T> 对象

  • 1、如果 key 已经在 map 中,插入失败,则返回一个 pair<iterator, bool> 对象,返回 pair 对象 first 是 key 所在结点的迭代器,second 是 false。
  • 2、如果 key 不在 map 中,插入成功,则返回一个 pair<iterator, bool> 对象,返回 pair 对象 first 是新插入 key 所在结点的迭代器,second 是 true。
  • 也就是说无论插入成功还是失败,返回 pair<iterator, bool> 对象的 first 都会指向 key 所在的迭代器。那么也就意味着 insert 插入失败时充当了查找的功能,正是因为这一点,insert 可以用来实现 operator[]。 需要注意的是这里有两个 pair,不要混淆了:

    • 一个是 map 底层红黑树节点中存的 pair<key, T>,
    • 另一个是 insert 返回值 pair<iterator, bool>。

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

    mapped_type& operator[] (const key_type& k);

    // operator[] 的内部实现
    mapped_type& operator[] (const key_type& k)
    {
    // 1、如果 k 不在 map 中,insert 会插入 k 和 mapped_type 默认值,
    // 同时 [] 返回结点中存储 mapped_type 值的引用,
    // 那么我们可以通过引用修改映射值。所以 [] 具备了插入+修改功能。
    // 2、如果 k 在 map 中,insert 会插入失败,但是 insert 返回 pair 对象的 first
    // 是指向 key 结点的迭代器,[] 同时返回结点中存储 mapped_type 值的引用,
    // 所以 [] 具备了查找+修改的功能。
    pair<iterator, bool> ret = insert({ k, mapped_type() });
    iterator it = ret.first;
    return it->second;
    }

    3.9 multimap和map的差异

    multimap 和 map 的使用基本完全类似,主要区别点在于 multimap 支持关键值 key 冗余,那么 ins ert/find/count/erase 都围绕着支持关键值 key 冗余有所差异,这里跟 set 和 multiset 完全一样。比如 find 时,有多个 key,返回中序第一个。其次就是 multimap 不支持 [],因为支持 key 冗余,[] 就只能支持插入了,不能支持修改。

    map相关算法题⭐️⭐️⭐️⭐️

    1,复制带随机指针的链表⭐⭐⭐⭐

    题目链接

    复制带随机指针的链表


    题目描述

    给你一个长度为 n 的链表,每个节点包含:

    • val:节点值
    • next:指向下一个节点
    • random:可以指向链表中的任意节点或者 null

    要求返回链表的深拷贝。 在这里插入图片描述


    解题思路

    使用 map 建立 原链表节点 -> 新链表节点 的映射。

    分两步:

  • 第一次遍历链表,复制所有节点,并建立原节点和新节点的对应关系。
  • 第二次遍历链表,根据 map 找到 random 指向的新节点,完成随机指针连接。

  • 我的解题代码

    /*
    // Definition for a Node.
    class Node {
    public:
    int val;
    Node* next;
    Node* random;

    Node(int _val) {
    val = _val;
    next = NULL;
    random = NULL;
    }
    };
    */

    class Solution {
    public:
    Node* copyRandomList(Node* head) {

    // 保存 原节点 -> 新节点 的映射
    map<Node*, Node*> m;

    Node* rhead = head;

    // 创建虚拟头节点,方便连接新链表
    Node* rethead = new Node(1);
    Node* ret = rethead;

    // 第一次遍历:复制所有节点
    while (head)
    {
    Node* tmp = new Node(head->val);

    rethead->next = tmp;
    rethead = tmp;

    // 建立映射关系
    m[head] = rethead;

    head = head->next;
    }

    // 删除虚拟头节点,返回真正的头节点
    Node* tmp = ret;
    ret = ret->next;
    delete tmp;

    // 保存原链表头,用于第二次遍历
    while (rhead)
    {
    // 根据映射关系设置 random 指针
    m[rhead]->random = m[rhead->random];

    rhead = rhead->next;
    }

    return ret;
    }
    };

    优化版,思路一样

    class Solution {
    public:
    Node* copyRandomList(Node* head) {
    map<Node*, Node*> nodeMap;
    Node* copyhead = nullptr, *copytail = nullptr;
    Node* cur = head;

    while (cur)
    {
    if (copytail == nullptr)
    {
    copyhead = copytail = new Node(cur->val);
    }
    else
    {
    copytail->next = new Node(cur->val);
    copytail = copytail->next;
    }

    // 原节点和拷贝节点 map kv 存储
    nodeMap[cur] = copytail;
    cur = cur->next;
    }

    // 处理 random
    cur = head;
    Node* copy = copyhead;
    while (cur)
    {
    if (cur->random == nullptr)
    {
    copy->random = nullptr;
    }
    else
    {
    copy->random = nodeMap[cur->random];
    }

    cur = cur->next;
    copy = copy->next;
    }

    return copyhead;
    }
    };


    没懂?看看大神的解题代码!!

    大神解题代码(深拷贝,直接原地复制数组)

    class Solution {
    public:
    Node* copyRandomList(Node* head) {

    if (head == nullptr)
    return nullptr;

    // 第一步:复制节点,并插入到原节点后面
    Node* cur = head;

    while (cur)
    {
    Node* copy = new Node(cur->val);

    copy->next = cur->next;
    cur->next = copy;

    cur = copy->next;
    }

    // 第二步:处理 random 指针
    cur = head;

    while (cur)
    {
    if (cur->random)
    cur->next->random = cur->random->next;

    cur = cur->next->next;
    }

    // 第三步:拆分链表
    Node* ret = head->next;
    cur = head;

    while (cur)
    {
    Node* copy = cur->next;

    cur->next = copy->next;

    if (copy->next)
    copy->next = copy->next->next;

    cur = cur->next;
    }

    return ret;
    }
    };

    2,前 K 个高频单词⭐⭐

    题目链接

    前 K 个高频单词


    题目描述

    给定一个字符串数组 words 和一个整数 k,返回出现频率最高的 k 个单词。

    答案需要按照:

  • 单词出现频率从高到低排序;
  • 如果频率相同,按照字典序从小到大排序。 在这里插入图片描述

  • 解题思路

    先使用哈希表统计每个单词出现的次数。

    然后利用优先级队列(堆)进行排序,通过自定义比较规则:

    • 出现次数多的优先级更高;
    • 出现次数相同时,字典序小的优先级更高。

    最后不断取出堆顶的 k 个单词即可。


    解题代码

    class Solution {

    // 自定义堆的比较规则
    struct cmp {

    bool operator()(pair<int, string> a, pair<int, string> b)
    {
    // 频率不同,频率高的优先级更高
    if (a.first != b.first)
    return a.first < b.first;

    // 频率相同,字典序小的优先级更高
    // priority_queue 默认大堆,所以这里反向比较
    return b.second < a.second;
    }
    };

    public:

    vector<string> topKFrequent(vector<string>& words, int k) {

    priority_queue<pair<int,string>,
    vector<pair<int,string>>,
    cmp> pq;

    // 统计每个单词出现次数
    unordered_map<string,int> m;

    for (auto it : words)
    m[it]++;

    // 将所有单词加入堆
    for (auto it : m)
    {
    pq.push({it.second, it.first});
    }

    vector<string> ret;

    // 取出前 k 个高频单词
    while (k)
    {
    string tmp = pq.top().second;
    pq.pop();

    ret.push_back(tmp);
    }

    return ret;
    }
    };


    没懂?看看大神的解题代码!!

    大神解题代码

    class Solution {
    public:

    vector<string> topKFrequent(vector<string>& words, int k) {

    unordered_map<string,int> cnt;

    // 统计出现次数
    for (auto& word : words)
    cnt[word]++;

    // 小根堆,只保留 k 个元素
    auto cmp = [](pair<string,int>& a, pair<string,int>& b)
    {
    // 频率低的优先弹出
    if (a.second != b.second)
    return a.second > b.second;

    // 频率相同时,字典序大的优先弹出
    return a.first < b.first;
    };

    priority_queue<pair<string,int>,
    vector<pair<string,int>>,
    decltype(cmp)> pq(cmp);

    for (auto& [word, count] : cnt)
    {
    pq.push({word, count});

    // 超过 k 个,删除当前最差元素
    if (pq.size() > k)
    pq.pop();
    }

    vector<string> ret;

    while (!pq.empty())
    {
    ret.push_back(pq.top().first);
    pq.pop();
    }

    // 小根堆取出顺序相反,需要反转
    reverse(ret.begin(), ret.end());

    return ret;
    }
    };

    下期预告

    AVL树

    结语

      本文到此结束,感谢大家的阅读!如果觉得本文对你有所帮助,欢迎点赞、收藏、关注,也欢迎在评论区一起交流讨论。   也欢迎订阅我的 深入理解 C++系列:从语法入门到底层原理,系统掌握现代 C++ 算法系列:从入门到精通,蓝桥杯、ACM、LeetCode 与面试算法全路线 快速复习系列:知识梳理、查漏补缺,考前冲刺必备 Java 速通系列:已学 C 语言,快速上手 Java,轻松备战期末考试


      愿每一次敲下键盘,都比昨天更进一步!

      愿每一行代码落下,都让未来多一种可能!

    赞(0)
    未经允许不得转载:171主机测评 » 深入理解C++系列(14)——map和set的使用
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址