【C++】map 与 set 的介绍与使用、力扣:692. 前K个高频单词

news/2025/1/18 6:54:02/

目录

一、关联式容器

二、键值对

三、set

3.1 set 的介绍

3.2 set 的使用

3.3. set 的使用举例

四、map

4.1 map的介绍

3.2 map 的使用

4.3 map的使用举例

五、经典练习题

1.set的使用

2.map的使用

思路一(稳定排序):

思路二(priority_queue):


一、关联式容器

在之前,我们接触过 vector、list、deque……,这些容器被称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那么什么是关联式容器呢?

关联式容器:关联式容器也是用来存储数据的,与序列式容器不同的是,其中存储的是<key,value>结构的键值对,在数据检索时比序列式容器效率更高。

二、键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量 key 、value。

key代表键值,value 表示与 key 对应的信息。比如:现在要建立一个英汉互译的字典,那该字典必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该英文单词,在字典中就可以找到其对应的中文含义。

SGI-STL中关于键值对的定义:

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

三、set

3.1 set 的介绍

这里是 set 的文档介绍:set的介绍_C++reference 以下是几个重要点。 

  1. set 是按照一定次序存储的容器
  2. 在 set 中,元素的 value 也标识它(value 就是 Key,类型为 T),并且每个 value 必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
  3. 在内部,set 中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。
  4. set 容器通过 key 访问单个元素的速度通常比 unordered_set 容易慢,但它们允许根据顺序对子集进行直接迭代。
  5. set 在底层是用二叉搜索树(红黑树)实现的。

3.2 set 的使用

1. set 的模板参数列表

T:set 中存放元素的类型,实际在底层存储<value,value>的键值对。

Compare:set 中元素默认按照小于来比较。

Alloc:set 中元素空间的管理方式,使用STL提供的空间配置器管理。

2. set 的构造函数

 3. set 的迭代器

iterator begin() 返回 set 中起始位置的迭代器
iterator end()返回 set 中最后一个元素的迭代器
iterator rbegin()返回第一个元素的迭代器,即end()
iterator rend()返回最后一个元素的迭代器,即begin()

4. set 的修改操作

函数声明功能介绍
pair<iterator,bool> insert (const value_type& x)

在set中插入元素x,实际是插入<x,x>构成的键值对,如果插入成功,返回<该元素在set中的位置,true>,如果插入失败,说明x在set中已经存在,返回<x在set中的位置,false>

void erase ( iterator position )
删除setposition位置上的元素
size_type erase ( const key_type& x )
删除set中值为x的元素,返回删除的元素的个数
void erase ( iterator fifirst, iterator last )
删除set[first, last)区间中的元素
void clear ( )
set中的元素清空
iterator find ( const key_type& x ) const
返回set中值为x的元素的位置,不存在返回end()
size_type count ( const key_type& x ) const
返回set中值为x的元素个数(multiset中使用)

3.3. set 的使用举例

四、map

4.1 map的介绍

map的文档简介 以下是几个重要点:

  1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
  2. 在map中,键值key通常用于排序和唯一的标识元素,而值value中存储与此key关联的内容。简直key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取名别称为pair
  3. 在内部,map中的元素总是按照及那只key进行排序的。
  4. map中通过简直访问但各国元素的熟读通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)
  5. map支持下标访问操作符,即在[]中放入key,就饿可以找到与key对应的value。
  6. map通常被实现为二叉搜索树(准确的说是红黑树)

3.2 map 的使用

1.map的模板参数说明

  •  key:键值对中key的类型
  • T:键值对中value的类型
  • Compare:比较器的类型。默认为less,如果存在特殊的比较,需要用户自己显示传入比较规则(函数指针或仿函数进行传递)

2.map的构造函数

 3.map的迭代器

iterator begin() 返回 map 中起始位置的迭代器
iterator end()返回 map 中最后一个元素的迭代器
iterator rbegin()返回第一个元素的迭代器,即end()
iterator rend()返回最后一个元素的迭代器,即begin()

4.insert

首先我们来看看insert的使用,首先要知道map的insert是插入pair类型的数据

 然后我们举例进行插入一下

void test_map1()
{//创建字典map<string, string> dict1;map<string, string> dict2;map<string, string> dict3;//插入数据——方式1、pair<string, string> kv1("sort", "排序");pair<string, string> kv2("insert", "插入");dict1.insert(kv1);dict1.insert(kv2);//插入数据——方式2、匿名对象dict2.insert(pair<string, string>("sort", "排序"));dict2.insert(pair<string, string>("insert", "插入"));//插入数据——方式3、make_pair()dict3.insert(make_pair("sort", "排序"));dict3.insert(make_pair("insert", "插入"));
}

插入方式3,make_pair其实是一个模板函数,就是为了方便我们进行pair结构的插入数据。

4. 下标访问操作符

这里我们实现一个统计水果次数功能的map,如下:

 但是这样的插入方式,非常的麻烦,所以map将下标访问操作符进行了重载,让其插入数据变得十分轻松。以下是 map中下标访问操作符:

功能解析:

  1. map中有这个key,返回value的引用。(查找、修改value)
  2. map中没有这个key,会插入pair(key,V()),并返回value的引用。(插入+修改)

其实下标访问操作符的本质是调用了 insert 函数,下面的文档中的实现方式:

这种方式其实不是太好理解,这里我们可以将其分为两步,就非常好理解了。

4.3 map的使用举例

因为返回的是其value的引用,所以我们可以对其进行赋值,这也是一种插入方式,也是最简单的一种插入方式。

 所以,在上面实现统计水果次数的map中,我们可以将其插入方式改为 [] 插入:

五、经典练习题

1.set的使用

题目链接:349. 两个数组的交集

题目介绍:

算法思路1:

  1. 因为是求交集,所以我们可以使用set对nums1、nums2进行排序+去重。
  2. 遍历set1,判断set1中的值是否存在于set2中,如果存在,则放到结果数组中,不存在则跳过(时间复杂度:O(N*N))。

算法思路2:

  1. 因为是求交集,所以我们可以使用set对nums1、nums2进行排序+去重。
  2. 同时处理两个区间,让it1指向的值与it2指向的值进行比较,如果相等则为交集,两个迭代器同时向后移动,如果不相等,结果小的迭代器进行向后移动(时间复杂度:O(N))

两种算法就是找交集的代码不同,这里一并给出源代码:

class Solution {
public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {set<int> s1;set<int> s2;//去重  for(auto& e: nums1)s1.insert(e);for(auto& e: nums2)s2.insert(e);//找交集vector <int >result;//方法1:for(auto& e: s1){if (s2.find(e)!=s2.end())result.push_back(e);}//============================================//方法2:set<int>::iterator it1=s1.begin();set<int>::iterator it2=s2.begin();while(it1!=s1.end()&&it2!=s2.end()){if (*it1==*it2){result.push_back(*it1);it1++,it2++;}else if (*it1>*it2) it2++;else it1++;}return result;}
};

2.map的使用

题目链接:692. 前K个高频单词

题目介绍:

这题的难点就在于出现频率相等的情况下,要按照字典序排序,这就意味着不能简单的使用一次排序解决。

思路一(稳定排序):

算法思路:

  1. 使用map根据其字典序进行排序,并统计单词的出现次数。
  2. 通过sort根据单词的出现次数进行排序,然后控制其稳定性(次数相同则不交换)

使用了map存储后,单词在map中都按出现次数进行排序,但是此时,如果我们使用一种稳定排序,让其在符合次数相同时,并进行字典序的排序,就可以完成最终的排序。

首先 sort 是一个的迭代器是随机迭代器(RandomAccess Iterator),而map双向迭代器(bidirectional iterator),所以我们要先将map中的数据放入到一个数组中。

 因为sort是快速排序,不稳定的排序,所以我们要编写仿函数控制其排序的结果。

 仿函数的比较规则:

  1. 如果出现次数多,则排在前面,返回true。
  2. 出现次数相同时再比较字典序,如果kv2的字典序小于kv1,则也返回true,交换。
  3. 其他情况直接返回false。
class Solution {
public:struct Greater{bool operator()(const pair<string,int>& kv1,const pair<string,int>& kv2){//按次数比较if (kv1.second > kv2.second)return true;//次数相同,按字典序比较。if (kv1.second==kv2.second&&kv1.first < kv2.first)return true;return false;}};vector<string> topKFrequent(vector<string>& words, int k) {map<string,int> countMap;for(auto& str:words){countMap[str]++;}//导数据,因为迭代器类型不同vector<pair<string,int>> sortV(countMap.begin(),countMap.end());sort(sortV.begin(),sortV.end(),Greater());vector <string> result;for(int i=0;i<k;i++){result.push_back(sortV[i].first);}return result;}
};

同样,我们可以直接使用库中的稳定排序(stable_sort)

 进行以下修改即可:

 

思路二(priority_queue):

算法思路:

  1. 使用map根据其字典序进行排序,并统计单词的出现次数。
  2. 再将数据放入priority_queue中,通过自定义仿函数建立k的数的小堆。
  3. 再将前k个数放入结果数组中。

既然要使用我们自己的仿函数,则模板参数这我们要额外注意,我们要按顺序传入参数,在仿函数前要先传入存储容器,因为此题使用的vector进行的存储,所以直接传入vector即可。

注意,第三个仿函数参数我们直接传入仿函数名即可,因为我们自己实现的仿函数一般不会再设置模板。不要写浑了(hhh)。

 仿函数的比较规则:

 TOP-K问题要建小堆,(如果建立大堆,只能求出最大的值,其他比它小的值进不了堆)。

  1. value小则返回true。
  2. value相同时再比较字典序,字典序如果kv1大于kv2,则返回true。
  3. 其他情况直接返回false。
struct Less {bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2){//按次数比较if (kv1.second < kv2.second)return true;//次数相同,按字典序比较。if (kv1.second == kv2.second && kv1.first > kv2.first)return true;return false;}
};

然后我们将数据插入到priority_queue中,我们可以使用迭代器区间进行初始化,也可以直接push插入数据。

因为要返回前k的数据。所以使用while循环,在priority_queue中取出前k个top数据,将其pair中的first放入结果数组中。

class Solution {
public:struct Less{bool operator()(const pair<string,int>& kv1,const pair<string,int>& kv2){//按次数比较if (kv1.second<kv2.second)return true;//次数相同,按字典序比较。if (kv1.second==kv2.second&&kv1.first>kv2.first)return true;return false;}};vector<string> topKFrequent(vector<string>& words, int k) {map<string,int> countMap;for(auto& str:words){countMap[str]++;}//迭代器区间初始化priority_queue <pair<string,int>,vector<pair<string,int>>,Less> maxHeap(countMap.begin(),countMap.end());vector<string> result;while(k--){result.push_back(maxHeap.top().first);maxHeap.pop();}return result;}
};

http://www.ppmy.cn/news/8262.html

相关文章

21. Serverless虚拟化遇到的内核device-mapper driver是什么

我在打造k8e的 Serverless 引擎时,选择了AWS的 Firecracker-microvm,遇到一个技术问题,它要求一定要支持内核device-mapper驱动,这是个什么东西?和 overlayfs 对比有啥优缺点要讲一讲。 Device-mapper是一个虚拟化块设备的框架,通过它构建出了逻辑卷(LVM)。主要通过dm…

STM32H750自制开发板调试经验

​本篇只是一个记录&#xff0c;没啥可看的。 STM32H750硬件相关 STM32H750可以通过USB-OTG下载程序&#xff0c;也可以使用SWD进行调试&#xff0c;所以设计板子得时候将PA13和PA12预留出来即可&#xff0c;后续也可以用作usb虚拟串口&#xff08;CDC&#xff09;功能或者模拟…

小程序框架

目录 一、框架 1.页面管理 2.基础组件 3.丰富的API 二、小程序视图层 1.响应的数据绑定 2.列表渲染 3.条件渲染 4.模板 三、小程序事件 1.什么是事件 2.事件的使用方式 3.使用 WXS 函数响应事件 4.事件详解 ① 事件分类 ② 普通事件绑定 ③ 绑定并阻止…

2022年学习机器人和人工智能的一些期待

2022年学习机器人和人工智能的一些体会 2023年即将到来&#xff0c;满满的期待。 做好规划是非常非常重要的&#xff0c;有时候甚至比认真做事本身更为重要。 《礼记中庸》&#xff1a;“凡事豫则立&#xff0c;不豫则废。言前定则不跲&#xff0c;事前定则不困&#xff0c;行…

django笔记《内置用户认证系统》

文章目录1 前言2 django.contrib.auth3 使用django的用户认证系统3.1 创建一个新的django项目3.2 做数据库迁移3.3 auth_user表结构3.4 创建一个新用户3.5 User对象3.5.1 创建用户 create_user3.5.2 request.user3.5.3 用户在视图函数中登录3.5.4 关键函数3.6 保护视图函数的方…

【leetcode】剑指offer1

&#x1f308;1.Pow(x,n) -100.0 < x < 100.0-2^31 < n < 2^31-1n 是一个整数-10^4 < x^n < 10^4思路分析&#xff1a; 暴力求解直接一个for循环n个x相乘解决&#xff0c;但是你拿那代码怎么好意思拿高薪&#xff1f; 所以而且那个的时间复杂度是O(n),效率并…

RHCE第一天之Linux例行性工作at、crontab详解

文章目录一、学习内容总结1、单一执行的例行性工作at2、循环执行的例行性工作crontab二、作业at和crontab的使用一、学习内容总结 1、单一执行的例行性工作at **概念&#xff1a;**指仅处理执行一次就结束了的工作。 要使用单一工作调度时&#xff0c;linux上面需要有负责这个…

【django】HttpRequest对象的属性和路由补充

文章目录一、HttpRequest对象的常用属性1、request.GET&#xff1a;获取查询字符串参数案例:特别注意&#xff1a;2、request.POST&#xff1a;post请求数据&#xff0c;只能获取表单参数3、request.body&#xff1a;请求body&#xff0c;响应结果为字节类型4、request.method&…