【C++】map和set在OJ中的应用

news2024/11/24 10:24:38

文章目录

  • 前言
  • 1. 剑指 Offer : 复杂链表(带随机指针)的复制
    • 1.1 思路分析(利用map搞)
    • 1.2 AC代码
  • 2. 前K个高频单词
    • 2.1 思路1+AC代码
    • 2.2 思路2+AC代码
    • 2.3 思路3+AC代码
  • 3. 两个数组的交集
    • 3.1 思路分析
    • 3.2 AC代码

前言

上一篇文章我们学习了map和set的使用,那学习了map和set之后,有些题目用它们做起来还是很爽的。

1. 剑指 Offer : 复杂链表(带随机指针)的复制

题目链接: link
在这里插入图片描述

如果大家看过我之前初阶数据结构的博客的话会发现这道题我们其实是讲过的,不过当时我们使用C语言搞的,说实话C语言实现起来还是挺麻烦的。
大家可以看一下之前这篇文章:

链接: 【初阶数据结构】——剑指 Offer : 复杂链表(带随机指针)的复制

1.1 思路分析(利用map搞)

我们再来一起回顾下之前C语言的做法
在这里插入图片描述

大家思考我们为什么要拷贝原链表的结点一个个链接到原链表结点的后面?

其实就建立了原链表结点与拷贝链表每个结点的一种映射关系,方便我们设置拷贝结点的random域。

那我们现在C++有了map,搞这个是不是很简单啊:

怎么做呢?
首先我们定义一个map,然后遍历原链表,依次拷贝结点,在map中建立源节点与拷贝结点的映射,并链接拷贝链表
在这里插入图片描述
然后,再遍历原链表设置拷贝结点的random域:
如果源节点的random指向空,那么拷贝结点random也指向空;如果源节点不指向空,那拷贝结点就指向map中对应源节点的random指向的结点对应的拷贝结点
在这里插入图片描述

1.2 AC代码

来写一下代码
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    Node* copyRandomList(Node* head) {
        map<Node*,Node*> m;
        Node* cur=head;
        Node* copyhead=nullptr, *copytail=nullptr;
        while(cur)
        {
            Node* copyNode=new Node(cur->val);
            m[cur]=copyNode;
            if(copytail==nullptr)
            {
                copyhead=copytail=copyNode;
            }
            else
            {
                copytail->next=copyNode;
                copytail=copytail->next;
            }
            cur=cur->next;
        }

        cur=head;
        while(cur)
        {
            if(cur->random==nullptr)
            {
                m[cur]->random=nullptr;
            }
            else
            {
                m[cur]->random=m[cur->random];
            }
            cur=cur->next;
        }
        return copyhead;
    }
};

2. 前K个高频单词

题目链接: link
在这里插入图片描述
给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。
返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序。

大家思考一下这道题怎么做?

2.1 思路1+AC代码

我们来分析一下:

那首先我们是不是应该对列表里面每个单词出现的次数做一个统计啊,然后要返回前k个出现次数最多的单词,那这是不是就是一个TOP-K问题啊。
那这道题其实比较需要注意的地方是如果有不同的单词出现相同的次数, 这些相同次数的单词要按字典顺序 排序。

那第一步我们可以先统计一下单词出现的次数

那现在统计次数对我们来说是不是很简单啊,直接用map就行了嘛,这不跟我们演示的那个统计水果出现次数的一样嘛。
在这里插入图片描述

那然后我们是不是要取到出现次数最多的前k个单词啊

那提到TOP-K的话,大家可能最先想到的就是用优先级队列去搞,这当然是一种方法,但是这里我们不打算讲这中解法。
那大家想一想还有没有其它方法?
🆗,我们是不是可以按照次数对所有单词进行一个排序啊,排个降序,然后前K个单词不就是要返回的结果嘛。
诶!那我们的map不是会“自动排序”(当然本质是因为中序遍历使得有序)嘛,是的,但是它是按照key的大小进行排(插入的时候比较的是key的大小)的,而我们统计出来的次数是不是放到value里面了。
但是我们想要的排序是按照次数排的,所以不行。

那要排序的话:

库里面不是有一个排序算法sort可以排嘛。
但是,我们的map不能用sort。
因为sort要求传入的迭代器必须是随机迭代器
在这里插入图片描述
而我们map的迭代器是双向迭代器
在这里插入图片描述
所以不行。

那怎么办呢?

🆗,我们可以把map里面的元素拷贝到vector里面再用sort排序。
在这里插入图片描述
但是呢,现在我们的vector里面存的是pair。
pair呢虽然也支持比较大小
在这里插入图片描述
它重载了这些比较运算符,但是大家看它的比较规则是我们想要的吗?
比如小于
在这里插入图片描述
它比较的时候是先看两个的first满不满足小于,不满足再去看second是否满足小于。
但是我们要按照次数比,即second比,另外sort默认还是升序,我们这里取TOP-K的话搞成降序比较好。
那既然不行,我们就可以自己写一个比较的仿函数(也可以写成函数传函数指针),因为sort是可以由我们自己指定比较方式的
在这里插入图片描述
那排好序的话我们取到前k个不就好了嘛(注意最终返回只要单词)
在这里插入图片描述
我们提交一下
在这里插入图片描述
🆗,只通过了一部分的测试用例

怎么回事呢?

大家还记不记得上面我们说了这道题特别要注意的就是题目要求如果有不同的单词出现相同的次数, 这些相同次数的单词要按字典顺序 排序。
我们放到map里面统计好次数,这时候虽然不是按次数进行排序,是按照key即first排序的嘛。
但是first不就是单词嘛,所以我们放到map里面之后单词的前后顺序其实就是按照字典顺序排好了。
我们后面的排序只需要按照次数再排一下就好了。

但是:

这是不是涉及到排序算是否稳定啊。
因为有可能有次数相同的单词,本来没按次数排之前它们的前后顺序是正确的(是按字典顺序的),但是如果按次数排序的时候,排序算法不稳定,是不是会导致这些次数相同的单词的前后顺序发生改变啊。
那这样得到的结果就不对了。
而我们刚才用的啥?
sort,底层是快排,不稳定,所以有些测试用例才没通过。

那我们怎么办?

🆗,那其实除了sort,算法库里面还提供了一个稳定的算法——stable_sort
在这里插入图片描述
在这里插入图片描述

所以:

在这里插入图片描述
在这里插入图片描述
把sort换成stable_sort就行了

class Solution {
public:
    struct Compare
    {
        bool operator()(const pair<string,int>& kv1, const pair<string,int>& kv2)
        {
            return kv1.second>kv2.second;
        }
    };
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string,int> countmap;
        for(auto& word:words)
        {
            countmap[word]++;
        }

        vector<pair<string,int>> v(countmap.begin(),countmap.end());
        stable_sort(v.begin(),v.end(),Compare());

        vector<string> ret;
        for(int i=0;i<k;i++)
        {
            ret.push_back(v[i].first);
        }
        return ret;
    }
};

2.2 思路2+AC代码

当然了,我们也不是必须用stable_sort才行。

既然sort不稳定,那我们可以让它变稳定:

在我们写的那个控制比较方式的仿函数里面加一个限制条件就行了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    struct Compare
    {
        bool operator()(const pair<string,int>& kv1, const pair<string,int>& kv2)
        {
            return kv1.second>kv2.second || (kv1.second==kv2.second&&kv1.first<kv2.first);
        }
    };
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string,int> countmap;
        for(auto& word:words)
        {
            countmap[word]++;
        }

        vector<pair<string,int>> v(countmap.begin(),countmap.end());
        sort(v.begin(),v.end(),Compare());

        vector<string> ret;
        for(int i=0;i<k;i++)
        {
            ret.push_back(v[i].first);
        }
        return ret;
    }
};

2.3 思路3+AC代码

那其实不用sort,我们也有办法搞:

我们可以用利用set排序。
怎么做呢?
前面我们统计好次数不是放到一个map里面了,那我们可以把它再放到一个set里面,那肯定要把整个pair放进去。
但是这样的话,sort默认的排序肯定是不行的,默认是升序,我们这里搞成降序的话比较好拿到前K个次数最多的单词。
另外,我们排序的时候要按次数比较,但是次数相同的时候要保证字典顺序小的在前面。

所以我们还是要自己写一个仿函数:

在这里插入图片描述
当然这个用我们上面写的那个就可以,因为也是存的pair,比较规则一样。

那然后我们把map里面的内容放到set里面,把前K个获取到就行了

在这里插入图片描述
在这里插入图片描述
但是提交这里报了一个很奇怪的错。
怎么回事呢?
🆗,注意我们写了这个仿函数,有时候他测试的时候会用const对象调用,所以以后写仿函数最后把const加上
在这里插入图片描述
在这里插入图片描述
就好了。

最后,大家想一下为什么我们这里用set(multiset也可以)而不用multimap(map的话有相同次数会去重)呢?

multimap不是也会排序吗?
🆗,因为用multimap的话他排序的时候控制的仿函数是按照key进行比较的
在这里插入图片描述
而我们要按次数比较,次数是value。

3. 两个数组的交集

题目链接: link
在这里插入图片描述
给我们两个数组,要求我们返回它们的交集,交集中每个元素必须是唯一的。

3.1 思路分析

这个题怎么做呢?

第一步,我们相对两个数组进行排序加去重(因为最终输出结果中元素要唯一,排序的话有助于我们后面找)。
那排序的话我们可以用sort,然后去重其实库里面也有去重算法——unique
在这里插入图片描述

但是,我们有必要有这两个吗?

我们直接一个set就搞定了
在这里插入图片描述
然后呢,其实算法库里面也是有求这些集合的算法的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述但是我们不建议大家这样写,题目就是想让我们自己设计算法呢。

那怎么求交集?

其实很简单
我们现在不是已经对两个数组进排序去重了嘛。
假设现在是这个样子
在这里插入图片描述
怎么求它们两个的交集呢?
很简单,双指针去遍历
在这里插入图片描述
两个元素相同,就是交集,同时++;
不相同,小的++;
有一个遍历完就结束
在这里插入图片描述

3.2 AC代码

来写一下代码
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        set<int> s1(nums1.begin(),nums1.end());
        set<int> s2(nums2.begin(),nums2.end());

        vector<int> ret;
        auto it1=s1.begin();
        auto it2=s2.begin();

        while(it1!=s1.end()&&it2!=s2.end())
        {
            if(*it1<*it2)
            {
                it1++;
            }
            else if(*it1>*it2)
            {
                it2++;
            }
            else
            {
                ret.push_back(*it1);
                it1++;
                it2++;
            }
        }
        return ret;
    }
};

在这里插入图片描述

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

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

相关文章

AI和ChatGPT:人工智能的奇迹

AI和ChatGPT&#xff1a;人工智能的奇迹 引言什么是人工智能&#xff1f;ChatGPT&#xff1a;AI的语言之王ChatGPT的工作原理ChatGPT的优势和挑战AI和ChatGPT的未来展望结论 引言 人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;是一项令人兴奋的…

收集到大量的名片怎么转为excel?

来百度APP畅享高清图片 参加完展会或集体会议&#xff0c;是不是收了一大堆名片&#xff0c;保管起来超级麻烦&#xff0c;还容易丢三落四&#xff1f;别急&#xff0c;我们有办法&#xff01;把名片转成电子版保存到电脑上就完美啦&#xff01;但要是名片数量有点多&#xff0…

Linux文本三剑客之awk

目录 前言 awk 1.认识awk 2.使用awk 2.1语法 2.2常用命令选项 2.3awk变量 2.3.1内置变量 2.3.2自定义变量 2.4printf命令 awk例题 前言 awk、grep、sed是linux操作文本的三大利器&#xff0c;合称文本三剑客&#xff0c;也是必须掌握的linux命令之一。三者的功能都是…

什么是全局代理,手机怎么设置全局代理

目录 什么是全局代理 全局代理的优缺点 优点 缺点 手机怎么设置全局代理 注意事项 总结 在计算机网络和信息安全中&#xff0c;全局代理是一种常用的技术手段&#xff0c;用于将网络流量通过代理服务器进行转发和处理。本文将介绍什么是全局代理&#xff0c;探讨全局代理…

Stable Diffusion - Candy Land (糖果世界) LoRA 提示词配置与效果展示

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/132145248 糖果世界 (Candy Land) 是一个充满甜蜜和奇幻的地方&#xff0c;由各种各样的糖果和巧克力构成。在糖果世界&#xff0c;可以看到&…

el-select 三级联动

一、效果图 二、思路&#xff1a;先请求一级select数据&#xff0c;通过选中的id请求二级数据&#xff0c;以此类推&#xff01; 三、代码 <template><div><el-card><el-form :inline"true"><el-form-item label"一级">&l…

如何用正确的姿势监听Android屏幕旋转

作者&#xff1a;37手游移动客户端团队 背景 关于个人&#xff0c;前段时间由于业务太忙&#xff0c;所以一直没有来得及思考并且沉淀点东西&#xff1b;同时组内一个个都在业务上能有自己的思考和总结&#xff0c;在这样的氛围下&#xff0c;不由自主的驱使周末开始写点东西&…

安卓:UDP通信

目录 一、介绍 网络通信的三要素&#xff1a; &#xff08;1&#xff09;、IP地址&#xff1a; IPv4: IPv6: IP地址形式&#xff1a; IP常用命令&#xff1a; IP地址操作类: &#xff08;2&#xff09;、端口&#xff1a; &#xff08;3&#xff09;、协议: UDP协…

【图论】单源最短路

算法提高课笔记。&#xff08;本篇还未更新完… 目录 单源最短路的建图方式例题热浪题意思路代码 信使题意思路代码 香甜的黄油题意思路代码 最小花费题意思路代码 最优乘车题意思路代码 昂贵的聘礼题意思路代码 单源最短路的建图方式 最短路问题可以分为以下两类&#xff1a…

红队钓鱼技术之LNK快捷方式

简介 lnk文件是用于指向其他文件的一种文件。这些文件通常称为快捷方式文件&#xff0c;通常它以快捷方式放在硬盘上&#xff0c;以方便使用者快速的调用。lnk钓鱼主要将图标伪装成正常图标&#xff0c;但是目标会执行shell命令 步骤 1.编写shell命令 首先新建一个文本文件t…

解码大众全新数字高尔夫8汽车CAN FD行驶功能电气架构

据在大众原厂的伙伴介绍&#xff0c;全新数字高尔夫8将在11月上市销售&#xff0c;目前高尔夫8在行驶功能电气架构上采用的CAN FD&#xff0c;在多媒体这一块采用的以太网&#xff0c;后续估计大部分类似同样MQBEvo平台的车型均会复制升级过来&#xff0c;那么&#xff0c;未来…

An unexpected error has occurred. Conda has prepared the above report

今日在服务器上创建anaconda虚拟环境的时候&#xff0c;出现了如下报错 An unexpected error has occurred. Conda has prepared the above report 直接上解决方案 在终端中输入如下指令 conda config --show-sources 如果出现以下提示&#xff0c;说明多了一个文件 输入以下…

基于CentOS 7构建LVS-DR集群

DIPVIPRIPClient192.169.41.139 LVS 192.168.41.134192.169.41.10RS1192.168.41.135RS2192.168.41.138 要求&#xff1a; node4为客户端&#xff0c;node2为LVS&#xff0c;node3和node4为RS。 1.配置DNS解析&#xff08;我这里使用本地解析&#xff09; 192.168.41.134 www.y…

谷粒商城第十天-获取分类属性分组(前端组件抽取父子组件交互)

目录 一、总述 1.1 前端思路 1.2 后端思路 二、前端部分 2.1 将分类树前端代码抽取成一个组件 2.2 使用elementUI的组件实现左右组件功能 2.3 使用事件机制进行组件通信 三、后端部分 四、总结 一、总述 说一下今天需要实现一个什么样子的功能&#xff1a; 很简单&am…

Vue2:组件高级(上)

Vue2&#xff1a;组件高级&#xff08;上&#xff09; Date: May 20, 2023 Sum: 组件样式冲突、data函数、组件通信、props、组件生命周期、vue3.x中全局配置axios 目标&#xff1a; 能够掌握 watch 侦听器的基本使用 能够知道 vue 中常用的生命周期函数 能够知道如何实现组…

基于自组织竞争网络的患者癌症发病预测(matlab代码)

1.案例背景 1.1自组织竞争网络概述 前面案例中讲述的都是在训练过程中采用有导师监督学习方式的神经网络模型。这种学习方式在训练过程中,需要预先给网络提供期望输出,根据期望输出来调整网络的权重,使得实际输出和期望输出尽可能地接近。但是在很多情况下,在人们认知的过程中…

分布式规则引擎框架的设计

MirAIe 规则引擎是一个可扩展且可扩展的规则引擎框架&#xff0c;允许用户对多个活动进行分组和自动化。 过去几年&#xff0c;在开发MirAIe 物联网平台时&#xff0c;我们意识到需要一个可扩展、可扩展的规则引擎框架。规则引擎使您能够对各种操作进行分组、管理和自动化&…

git【潦草学习】

初始配置git 查询版本号 初次使用git前配置用户名与邮箱地址 git config --global user.name "your name" git config --global user.email "your email" git config -l 发现最后两行多出了用户名和邮箱&#xff0c;说明配置成功

对指针变量引用以及自定义类型引用的认识

#include <iostream> using namespace std; #include <iomanip>typedef int G[10]; //自定义类型void test(int* p); // void test_0(int &p); //表明p是一个int型变量的别名 void test_1(G &pG); //表明pG是一个G类型变量的别名 void test_2(int*…

PMP备考心得分享

备考PMP考试是一段充满挑战和成长的旅程。参加某机构的PMP培训课程&#xff0c;有国内PMP考培资深讲师的授课&#xff0c;以及班主任的周期监督管理&#xff0c;无疑是我备考成功的重要支撑。在这个过程中&#xff0c;我积累了许多宝贵的经验和心得&#xff0c;现在将它们分享给…