代码随想录算法训练营第十天 | 239.滑动窗口最大值、347.前K个高频元素
代码随想录算法训练营第十天 | 239.滑动窗口最大值、347.前K个高频元素
文章目录
- 代码随想录算法训练营第十天 | 239.滑动窗口最大值、347.前K个高频元素
- 1 LeetCode 239.滑动窗口最大值
- 2 LeetCode 347.前K个高频元素
1 LeetCode 239.滑动窗口最大值
题目链接:https://leetcode.cn/problems/sliding-window-maximum/description/
给你一个整数数组
nums
,有一个大小为k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k
个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7] 解释: 滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 31 [3 -1 -3] 5 3 6 7 31 3 [-1 -3 5] 3 6 7 51 3 -1 [-3 5 3] 6 7 51 3 -1 -3 [5 3 6] 7 61 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1 输出:[1]
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
这道题目很难,对于第一次做的人来说,力扣上面的困难题目还是很有挑战性的,这道题目考察的就是单调队列的应用,很多人可能会想到优先级队列,在脑袋里面大概想了一下可能觉得可以,但其实不行,因为优先级队列会打乱顺序,就比如nums = [1,3,-1,-3,5,3,6,7], k = 3
,刚开始排序[3,1,-1],没问题,最大值在队头,然后我们还需要向右移动,然后弹出1,可是优先级队列中1在队列的中间,无法弹出,所以就无法实现找滑动窗口中的最大值。
我们可以利用双端队列来构造一个单调队列来解决这道题目。
- 创建一个双端队列(Deque)来存储元素的索引,而不是存储元素的值,这是因为我们需要在队列中比较索引,以确定元素是否在窗口内,创建一个空列表
result
来存储最终的结果。 - 我们开始遍历数组
nums
,逐个处理每个元素。 - 在每次遍历之前,检查队列的首元素是否已经超出了当前窗口的范围,如果队列的首元素对应的索引小于当前索引减去窗口大小
k
加 1,就将队列的首元素弹出(出队),因为它不再在窗口内。 - 继续检查队列中的元素,如果队列中的元素对应的数组值小于等于当前元素的值,将它们从队列的尾部弹出,因为它们不可能是窗口内的最大值,我们的目标是确保队列中的元素是按照递减顺序排列的,以便窗口内的最大值总是在队列的首元素位置。
- 将当前元素的索引添加到队列的尾部。
- 当我们遍历到达满足窗口大小的位置(即当前索引大于等于
k-1
),就可以获取窗口内的最大值。这是因为我们确保队列的首元素是当前窗口内的最大值的索引。将队列的首元素对应的值添加到结果列表result
中。 - 继续遍历整个数组,重复上述步骤直到遍历完成。
- 最后返回结果列表
result
,其中包含了每个窗口内的最大值。
思路清楚,下面我们来写一下代码:
(1)Python版本代码
from collections import deque
class Solution:def maxSlidingWindow(self, nums, k):result = [] # 用于存储最终的滑动窗口最大值window = deque() # 创建一个双端队列用于存储窗口内的元素索引n = len(nums)for i in range(n): if window and window[0] < i - k + 1: # 移除队列中超出窗口范围的索引window.popleft()while window and nums[i] >= nums[window[-1]]: # 移除队列中比当前元素小的元素索引window.pop()window.append(i) # 将当前元素的索引加入队列if i >= k - 1: # 当窗口形成后,将队列的首元素对应的值加入结果列表result.append(nums[window[0]])return result
下面是代码随想录中的代码:
from collections import dequeclass MyQueue: #单调队列(从大到小def __init__(self):self.queue = deque() #这里需要使用deque实现单调队列,直接使用list会超时#每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。#同时pop之前判断队列当前是否为空。def pop(self, value):if self.queue and value == self.queue[0]:self.queue.popleft()#list.pop()时间复杂度为O(n),这里需要使用collections.deque()#如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。#这样就保持了队列里的数值是单调从大到小的了。def push(self, value):while self.queue and value > self.queue[-1]:self.queue.pop()self.queue.append(value)#查询当前队列里的最大值 直接返回队列前端也就是front就可以了。def front(self):return self.queue[0]class Solution:def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:que = MyQueue()result = []for i in range(k): #先将前k的元素放进队列que.push(nums[i])result.append(que.front()) #result 记录前k的元素的最大值for i in range(k, len(nums)):que.pop(nums[i - k]) #滑动窗口移除最前面元素que.push(nums[i]) #滑动窗口前加入最后面的元素result.append(que.front()) #记录对应的最大值return result
(2)C++版本代码
#include <vector>
#include <deque>class Solution {
public:std::vector<int> maxSlidingWindow(std::vector<int>& nums, int k) {std::vector<int> result;std::deque<int> window;int n = nums.size();for (int i = 0; i < n; ++i) {// 移除队列中超出窗口范围的索引if (!window.empty() && window.front() < i - k + 1) {window.pop_front();}// 移除队列中比当前元素小的元素索引while (!window.empty() && nums[i] >= nums[window.back()]) {window.pop_back();}// 将当前元素的索引加入队列window.push_back(i);// 当窗口形成后,将队列的首元素对应的值加入结果列表if (i >= k - 1) {result.push_back(nums[window.front()]);}}return result;}
};
- 时间复杂度: O(n)
- 空间复杂度: O(k)
后续研究了一下,发现这题可以用优先队列实现的,也就是用大根堆实现,下面直接给出我学到的代码,感兴趣的朋友可以研究一下:
#include <vector> #include <queue> #include <utility>class Solution { public:std::vector<int> maxSlidingWindow(std::vector<int>& nums, int k) {std::vector<int> result;std::priority_queue<std::pair<int, int>> pq; // 存储元素值和索引的大根堆for (int i = 0; i < nums.size(); ++i) {while (!pq.empty() && pq.top().second <= i - k) {pq.pop(); // 移除不在窗口内的元素}pq.push({nums[i], i});if (i >= k - 1) {result.push_back(pq.top().first); // 将窗口的最大值加入结果列表}}return result;} };
此时的时间复杂度到达了O(log k)。
2 LeetCode 347.前K个高频元素
题目链接:https://leetcode.cn/problems/top-k-frequent-elements/description/
给你一个整数数组
nums
和一个整数k
,请你返回其中出现频率前k
高的元素。你可以按 任意顺序 返回答案。示例 1:
输入: nums = [1,1,1,2,2,3], k = 2 输出: [1,2]
示例 2:
输入: nums = [1], k = 1 输出: [1]
提示:
1 <= nums.length <= 105
k
的取值范围是[1, 数组中不相同的元素的个数]
- 题目数据保证答案唯一,换句话说,数组中前
k
个高频元素的集合是唯一的**进阶:**你所设计算法的时间复杂度 必须 优于
O(n log n)
,其中n
是数组大小。
一般来说,题目出现统计元素频率以及找出频率前K个元素,我们就要想到map和堆的结合运用,其中map很适合用来统计元素频率,然后堆也很适合维系一个单调的K个元素的排序集合,然后对数据集不断地遍历,不断地更新维系地集合。
堆有两种形式,一种是大顶堆(也叫大根堆),另一种是小顶堆(也叫小根堆),相信学过408的朋友对堆这种数据结构应该不陌生,那么在本题中我们应该选择哪一种形式的堆呢?答案是小顶堆,为什么?因为如果选择大顶堆,那么我们在每次加入元素之后,判断堆中元素是否超过K个(因为我们只需要维系K个元素即可),如果超过我们就需要将堆顶元素弹出,但是大顶堆的话此时堆顶元素是最大值,最后遍历完我们其实收集的是前K个低频元素,刚好相反了,因此我们就需要选择小顶堆来实现(可以手动画图感受一下,堆也就是一颗完全二叉树)。
(1)Python版本代码
import heapqclass Solution:def topKFrequent(self, nums, k):# 统计每个元素的频率hashmap = {}for num in nums:hashmap[num] = hashmap.get(num, 0) + 1# 使用最小堆来存储所有元素heap = []for key, value in hashmap.items():heapq.heappush(heap, (value, key)) # 存储频率和元素# 弹出除了频率最高的k个元素之外的所有元素while len(heap) > k:heapq.heappop(heap)# 提取结果return [key for _, key in heap]if __name__ == '__main__':s = Solution()nums = list(map(int, input().split()))k = int(input())print(s.topKFrequent(nums, k))
(2)C++版本代码
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <utility>
#include <functional>class Solution {
public:std::vector<int> topKFrequent(std::vector<int>& nums, int k) {// 统计每个元素的频率std::unordered_map<int, int> hashmap;for (int num : nums) {++hashmap[num];}// 使用最小堆来存储所有元素std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>, std::greater<std::pair<int, int>>> heap;for (auto& it : hashmap) {heap.push({it.second, it.first});if (heap.size() > k) {heap.pop();}}std::vector<int> result;while (!heap.empty()) {result.push_back(heap.top().second);heap.pop();}return result;}
};int main() {Solution s;std::vector<int> nums;int num;while (std::cin >> num) {nums.push_back(num);if (std::cin.peek() == '\n') break;}int k;std::cin >> k;std::vector<int> result = s.topKFrequent(nums, k);for (int i : result) {std::cout << i << " ";}std::cout << std::endl;return 0;
}
- 时间复杂度:O(nlogn)
- 空间复杂度:O(n)
相关文章:
代码随想录算法训练营第十天 | 239.滑动窗口最大值、347.前K个高频元素
代码随想录算法训练营第十天 | 239.滑动窗口最大值、347.前K个高频元素 文章目录 代码随想录算法训练营第十天 | 239.滑动窗口最大值、347.前K个高频元素1 LeetCode 239.滑动窗口最大值2 LeetCode 347.前K个高频元素 1 LeetCode 239.滑动窗口最大值 题目链接:https…...

【Godot4自学手册】第五节用GDScript语言让主人公动起来
GDScript 是Godot自带的编程语言,用于编写游戏逻辑,它是一种高级面向对象的指令式编程语言,使用渐进类型,专为 Godot 构建。在这一小节里,我将自学用GDScript语言控制主人公的行走和攻击。 一、给Player节点添加GDScr…...

被问到Tomcat是什么该怎么回答?他还有一个好帮手JDK你知道吗?
目录 Tomcat简介: 使用建议: Tomcat好帮手---JDK Tomcat和JDK的关系 安装JDK 1.打开浏览器输入网址 Oracle | Cloud Applications and Cloud Platform 进入Oracle官网 2、在官网首页菜单栏,点击产品,在硬件和软件中找到Java࿰…...

【Web前端实操11】定位实操_照片墙(无序摆放)
设置一个板块,将照片随意无序摆放在墙上,从而形成照片墙。本来效果应该是很唯美好看的,就像这种,但是奈何本人手太笨,只好设置能达到照片墙的效果就可。 代码如下: <!DOCTYPE html> <html lang&…...

图像处理------调整色调
什么是色调? 色调,在画面上表现思想、感情所使用的色彩和色彩的浓淡。分为暖色调和冷色调。 from cv2 import destroyAllWindows, imread, imshow, waitKey#创建棕褐色色调 def make_sepia(img, factor: int):pixel_h, pixel_v img.shape[0], img.shap…...

【操作系统】实验七 显示进程列表
🕺作者: 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux 😘欢迎关注:👍点赞🙌收藏✍️留言 🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要&…...

[实战]加密传输数据解密
前言 下面将分享一些实际的渗透测试经验,帮助你应对在测试中遇到的数据包内容加密的情况。我们将以实战为主,技巧为辅,进入逆向的大门。 技巧 开局先讲一下技巧,掌握好了技巧,方便逆向的时候可以更加快速的找到关键函数…...
yarn install 报错 证书过期 Certificate has expired
“Certificate has expired” 的意思是证书已过期。这通常是指数字证书在其有效期限之前已经失效了。数字证书通常用于加密和保护网络通信,以及验证网站的身份。如果证书已经过期,那么使用该证书的网站或服务可能会受到安全威胁。为了保证安全࿰…...

多流转换 (分流,合流,基于时间的合流——双流联结 )
目录 一,分流 1.实现分流 2.使用侧输出流 二,合流 1,联合 2,连接 三,基于时间的合流——双流联结 1,窗口联结 1.1 窗口联结的调用 1.2 窗口联结的处理流程 2,间隔联结 2.1 间隔联…...

Linux破解密码
破解root密码(Linux 7) 1、先重启——e 2、Linux 16这一行 末尾加rd.break(不要回车)中断加载内核 3、再ctrlx启动,进入救援模式 4、mount -o remount,rw /sysroot/——(mount挂载 o——opti…...
ABAP 批导demo调用SM30表维护demo
ABAP 批导demo&调用SM30表维护demo &--------------------------------------------------------------------- *& Report ZPP036 &--------------------------------------------------------------------- *& &-----------------------------------…...
Mysql 文件导入与导出
i/o 一、导出(mysqldump)<一>、导出sql文件<二>、导出csv文件 二、导入(load)三、常见报错The Mysql server is running with the --secure-file-priv option so it cannot execute this statement 一、导出(mysqldump) <一>、导出sql文件 1、整库 mysqld…...
《每天十分钟》-红宝书第4版-迭代器与生成器
理解迭代 计数循环就是一种最简单的迭代 for (let i 1; i < 10; i) { console.log(i); }迭代之前需要事先知道如何使用数据结构。数组中的每一项都只能先通过引用取得数组对象,然后再通过[]操作符取得特定索引位置上的项。这种情况并不适用于所有数据结构。遍…...

1、【vue篇】vue框架快速上手
注意事项: methods必须要加s 导入vue:<script src"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>导入Axios:<script src"https://unpkg.com/axios/dist/axios.min.js"></script> 简单Vue程序…...

Unity 编辑器篇|(九)编辑器美化类( GUIStyle、GUISkin、EditorStyles) (全面总结 | 建议收藏)
目录 1. GUIStyle1.1 参数总览1.2 样式代码 2. GUISkin2.1 参数总览2.2 创建自定义Skin 3. EditorStyles2.1 参数总览1.2 反射获取所有EditorStyles 1. GUIStyle GUIStyle是一个用于定制GUI控件样式的类,它包含了控件的外观属性,如字体、颜色、背景等。…...
Spring Boot Starters
Spring Boot Starters 概述 Spring Boot Starters是一系列为特定应用场景预设的依赖管理和自动配置方案。每个Starter都是为了简化特定类型的项目构建和配置。例如,spring-boot-starter-web是为创建基于Spring MVC的Web应用程序而设计的。 Starter的结构 一个典型…...
Qt防止创建窗口抢焦点
问题是,当我在 Qt 中打开一个新窗口时,它会自动窃取前一个应用程序的焦点。 有什么办法可以防止这种情况发生吗? setAttribute(Qt::WA_ShowWithoutActivating);这会强制窗口不激活。即使有Qt::WindowStaysOnTopHint flag 出处: S…...

shared_ptr 与 unique_ptr 的转换 笔记
推荐B站文章: 6.shared_ptr与unique_ptr_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV18B4y187uL?p6&vd_sourcea934d7fc6f47698a29dac90a922ba5a3我的往期文章: 独占指针:unique_ptr 与 函数调用-CSDN博客https://blog.csdn.n…...

python windows和linux 文件同步
在Python中,可以使用paramiko库来实现Windows和Linux之间的文件同步。paramiko是一个用于SSH连接的Python库,可以用于在Windows和Linux之间进行文件传输。 以下是一个简单的示例代码,演示如何使用paramiko库在Windows和Linux之间同步文件&am…...

【数据结构】72变的双端队列
双端队列 前言一、双端队列1.1 双端队列的定义1.2 输入受限的双端队列1.3 输出受限的双端队列1.5 输入输出都受限的双端队列1.6 小结 二、双端队列的使用2.1 双端队列的出队序列——暴力求解2.1.1 栈的出栈序列2.1.2 输入受限的双端队列2.1.3 输出受限的双端队列2.1.4 输入输出…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...