滑动窗口最大值(LeetCode 239)

文章目录

  • 1.问题描述
  • 2.难度等级
  • 3.热门指数
  • 4.解题思路
    • 方法一:暴力法
    • 方法二:优先队列
    • 方法三:单调队列
  • 参考文献

1.问题描述

给你一个整数数组 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       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

示例 2:

输入:nums = [1], k = 1
输出:[1]

提示:

1 <= nums.length <= 10^5
10^4 <= nums[i] <= 10^4
1 <= k <= nums.length

2.难度等级

Hard。

3.热门指数

★★★★☆

出题公司:阿里、腾讯、字节。

4.解题思路

方法一:暴力法

遍历所有的滑动窗口,通过遍历窗口内的所有值获取窗口最大值。

那一共有多少个滑动窗口呢,小学题目,一共可以得到 n-k+1 个滑动窗口。其中 n 为数组长度,k 为滑动窗口大小。

假设 nums = [1,3,-1,-3,5,3,6,7] 和 k = 3,那么窗口数为 n-k+1 = 6。

在这里插入图片描述

时间复杂度: O((n-k+1)k)。

空间复杂度:O(1)。不算结果集占用的内存,使用一个变量表示滑动窗口的最大值,所以空间复杂度为 O(1)。

注意: 该解法在 LeetCode 会「超出时间限制」。

下面以 Golang 为例给出实现。

func maxSlidingWindow(nums []int, k int) []int {
    var r []int
    for i := k; i <= len(nums); i++ {
        max := slices.Max(nums[i-k:i])
        r = append(r, max)
    }
    return r
}

方法二:优先队列

好一些的做法,使用优先队列(堆)来做。

在一个堆中,根节点是最大(或最小)节点。如果根节点最小,称之为小顶堆(或小根堆),如果根节点最大,称之为大顶堆(或大根堆)。注意堆的左右孩子没有大小的顺序。

我们可以构建维护一个大顶堆,堆顶元素就是滑动窗口中的最大值。每一次窗口滑动的时候,我们都需要将新进入窗口的元素加到堆中。

注意: 因为堆不支持删除指定的元素,删除元素只能将堆顶的元素弹出,所以在移动窗口时,左边离开窗口的元素不着急从堆中删除,而是当堆顶元素不在窗口中时,不断地移除堆顶的元素,直到堆顶的元素出现在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。

为了方便判断堆顶元素与滑动窗口的位置关系,我们在堆中存储二元组 (num, index),堆的元素是下标 index,权重是下标对应的值 num。

时间复杂度: 时间复杂度:O(nlog⁡n),其中 n 是数组 nums 的长度。在最坏情况下,数组 nums 中的元素单调递增,那么最终优先队列中包含了所有元素,没有元素被移除。由于将一个元素放入优先队列的时间复杂度为 O(log⁡n),因此总时间复杂度为 O(nlog⁡n)。

空间复杂度: O(n),即为优先队列需要使用的空间。

下面以 Golang 为例给出实现。

// a 表示数组,用于表示堆中元素的权重。
var a []int

// 堆,需要实现 heap.Interface 接口。
// 使用 []int 作为堆的存储结构,其中存储数组 nums 的下标。
type hp struct{ sort.IntSlice }
func (h hp) Less(i, j int) bool  { return a[h.IntSlice[i]] > a[h.IntSlice[j]] }
func (h *hp) Push(v interface{}) { h.IntSlice = append(h.IntSlice, v.(int)) }

// 注意:heap 包的源码在 Pop 前会将堆顶元素与最后一个元素交换后再调用该函数。
func (h *hp) Pop() interface{}   { s := h.IntSlice; v := s[len(s)-1]; h.IntSlice = s[:len(s)-1]; return v }

func maxSlidingWindow(nums []int, k int) []int {
    a = nums
    q := &hp{make([]int, k)}
    for i := 0; i < k; i++ {
        q.IntSlice[i] = i
    }
    heap.Init(q)

    n := len(nums)
    r := make([]int, 1, n-k+1)
    r[0] = nums[q.IntSlice[0]]
    for i := k; i < n; i++ {
        heap.Push(q, i)
        // 当堆顶元素不在窗口中时不断弹出,直至堆顶的元素出现在滑动窗口中。
        for q.IntSlice[0] <= i-k {
            heap.Pop(q)
        }
        r = append(r, nums[q.IntSlice[0]])
    }
    return r
}

方法三:单调队列

能不能在线性时间内求解该问题呢?

我们可以通过一个单调队列保存当前窗口的最大值以及「在窗口最大值后面递减的值」。

为了便于判断队首元素是否超出窗口范围,所以队列中保存数组元素下标。

  1. 首先初始化第一个窗口对应的单调队列。遍历窗口元素:
  • 如果大于等于队尾元素,则删除队尾元素,然后将元素下标存入队尾。
  • 如果小于队尾元素,则直接入队列。
  1. 然后获取队首元素作为第一个窗口的最大值。

  2. 当滑动窗口向右移动时,我们需要把一个新的元素放入队列。放入方式与初始化第一个窗口对应的单调队列相同。

  3. 每移动一次窗口,都需要判断队首元素下标是否已经不在当前窗口,如果不在则移除。

为了可以同时弹出队首和队尾的元素,我们需要使用「双端队列」。

时间复杂度: O(n),其中 n 是数组 nums 的长度。每一个下标恰好被放入队列一次,并且最多被弹出队列一次,因此时间复杂度为 O(n)。

空间复杂度: O(k)。与方法一不同的是,在方法二中我们使用的数据结构是双向的,因此「不断从队首弹出元素」保证了队列中最多不会有超过 k+1 个元素,因此队列使用的空间为 O(k)。

下面以 Golang 为例给出实现:

func maxSlidingWindow(nums []int, k int) []int {
    // 双端队列。
    q := []int{}
    push := func(i int) {
        for len(q) > 0 && nums[i] >= nums[q[len(q)-1]] {
            q = q[:len(q)-1]
        }
        q = append(q, i)
    }

    // 初始化第一个窗口对应的队列。
    for i := 0; i < k; i++ {
        push(i)
    }

    n := len(nums)
    r := make([]int, 1, n-k+1)
    r[0] = nums[q[0]]
    
    // 移动窗口。
    for i := k; i < n; i++ {
        push(i)
        // 如果队列首部元素下标不在窗口则移除。
        if q[0] <= i-k {
            q = q[1:]
        }
        r = append(r, nums[q[0]])
    }
    return r
}

参考文献

239. 滑动窗口最大值

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

信必优亮相2023粤港澳大湾区服务贸易大会

12月6日至8日&#xff0c;以“服务数字化策源地 贸易数字化领航区”为主题的2023粤港澳大湾区服务贸易大会&#xff08;简称“大湾区服贸会”&#xff09;在珠海举行。本次大会由广东省人民政府、香港特别行政区政府、澳门特别行政区政府共同指导&#xff0c;广东省商务厅、珠海…

人工智能与低代码:前端技术的双重变革

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;和低代码开发平台已经成为当下热门的话题。在前端技术领域&#xff0c;这两大技术的崛起正在悄然改变开发模式&#xff0c;提高开发效率&#xff0c;降低技术门槛。本文将从以下几个方面&#xff0c;详细探讨…

LangChain学习三:链-实战

文章目录 上一节内容&#xff1a;LangChain学习二&#xff1a;提示-实战&#xff08;下半部分&#xff09;学习目标&#xff1a;明白链是什么&#xff1f;有哪些&#xff1f;怎么用&#xff1f;学习内容一&#xff1a;介绍学习内容二&#xff1a;有那些学习内容三&#xff1a;实…

【答案】2023年国赛信息安全管理与评估第三阶段夺旗挑战CTF(网络安全渗透)

【答案】2023年国赛信息安全管理与评估第三阶段夺旗挑战CTF&#xff08;网络安全渗透&#xff09; 全国职业院校技能大赛高职组信息安全管理与评估 &#xff08;赛项&#xff09; 评分标准 第三阶段 夺旗挑战CTF&#xff08;网络安全渗透&#xff09; *竞赛项目赛题* 本文…

【AntDesign】Modal模态窗带来的缓存问题

背景 : 使用antdesign modal写模态窗, 列表点击"编辑"可以打开模态窗, 并对里面的文字和图片进行修改 问题 : 每次关闭模态窗后, 点击其他数据进行修改, 会发现图片这栏有时候有数据, 有时候会为空, 明明已经传了imgUrl过来了。 modal模态窗具有缓存问题&#xff0…

人工智能驱动的智慧城市:科技之光照亮未来城市发展

导言 人工智能在智慧城市建设中扮演着关键角色&#xff0c;通过智能化、自动化的手段&#xff0c;为城市提供高效、智能的管理和服务。本文将深入研究人工智能在智慧城市中的应用、创新技术以及对城市未来发展的引领作用。 智慧城市是利用先进的信息技术和大数据分析手…

VR智慧眼:为各行业打造3D数字化业务协同平台

自改革开放以来&#xff0c;城镇化建设一直在不断推进实施&#xff0c;如今各城市化速度虽然在不断加快&#xff0c;但随之而来的部分城市开始出现资源短缺、环境污染、交通拥堵、安全隐患等问题&#xff0c;因此为了满足智慧城市大型区域场景数字化升级需求&#xff0c;助力区…

基于导数Zernike多项式拟合技术的干涉测量二维相位展开算法(原文翻译)

Zixin Zhao1&#xff0c;Hong Zhao1、Lu Zhang 1&#xff0c;Fen Gao2&#xff0c;Yuwei Qin3&#xff0c;Hubing Du 摘要: 我们提出了一种适用于一般干涉测量应用的相位展开方法。所提出的方法依赖于导数泽尼克多项式拟合&#xff08;DZPF&#xff09;技术&#xff0c;其中相…

verilog高级语法-原语-ibuf-obuf-LUT

概述&#xff1a; 原语直接操作FPGA的资源&#xff0c;对FPGA的结构更加清晰&#xff0c;使用原语之前需要对FPGA的资源进行了解&#xff0c;本节为初识原语 学习内容 1. 输入缓冲原语 IBUF 2. 输出缓冲原语 OBUF 3. 查找表原语 LUT 1. IBUF&#xff0c;OBUF原语简介 …

vscode的文件和文件夹的警告标志如何消去

由于平时用vscode写一些java的小demo, 但是这个vscode的警告和错误管理很奇怪, 这个警告信息会显示在这个侧边的文件和文件夹中, 我上网上找能不能把这个给去掉的办法, 找了半天没找到。 于是我就自己去查了一下这个vscode的设置, 真让我找到了这方面的开关, 把下面的这个关闭…

智能物联网(IoT)VS AI物联网(AIoT)

#IoT# #AIoT# 智能物联网&#xff08;IoT&#xff09;和AI物联网&#xff08;AIoT&#xff09;区别 概念&#xff1a; 物联网&#xff08;IoT&#xff09;&#xff1a;即“万物相连的互联网”&#xff0c;是在互联网基础上延伸和扩展的网络&#xff0c;将各种信息传感设备与网…

点云格式转换:将 ros PointCloud2格式数据转为livox CustomMsg格式

将 ros PointCloud2格式数据转为livox CustomMsg格式 前言点云格式PointCloud2 点云格式livox CustomMsg 点云格式 将 ros PointCloud2格式数据转为livox CustomMsg格式测试 前言 览沃科技有限公司&#xff08;Livox&#xff09;成立于2016年。为了革新激光雷达行业&#xff0…

Docker及其使用思维导图

Docker的架构 构建分发运行镜像 Client&#xff08;客户端&#xff09;&#xff1a;是Docker的用户端&#xff0c;可以接受用户命令和配置标识&#xff0c;并与Docker daemon通信。Images&#xff08;镜像&#xff09;&#xff1a;是一个只读模板&#xff0c;含创建Docker容器…

JAVA对文档加密

当 Word 文档中包含无法公开的机密信息时&#xff0c;我们可以对其进行加密&#xff0c;使其在没有密码的情况下无法打开。本文将向您介绍如何使用 Spire.Doc for Java 加密 Word 文档和移除 Word 密码保护。 加密 Word 文档删除 Word 密码保护 安装 Spire.Doc for Java 首先…

Linux系统中如何开启和配置OpenGauss数据库的远程连接

文章目录 前言1. Linux 安装 openGauss2. Linux 安装cpolar3. 创建openGauss主节点端口号公网地址4. 远程连接openGauss5. 固定连接TCP公网地址6. 固定地址连接测试7. 结语 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍…

分布式 SQL 数据库与表格优化技术

分布式 SQL 数据库会将应用程序数据存储在多个节点上&#xff0c;从存储和计算的角度提高了可扩展性。这种分布意味着某些应用程序请求&#xff0c;包括 JOIN 操作和聚合&#xff0c;可能跨多个数据库节点&#xff0c;可能导致数据在网络中的传输。 为了减轻网络延迟对整体应用…

Echarts 热力图与折线图的结合

热力图与折线图结合使用(文末含源码) 这种需求并不多见&#xff0c;遇到后第一时间翻看了Echars官方文档&#xff0c;并没有发现类似的例子。于是自己动手合并了双轴&#xff0c;后发现折线图会被遮盖。经过排查发现了一个关键参数&#xff1a;visualMap的配置。这个配置在热力…

什么是客服RPA?客服RPA应用场景有哪些?客服RPA解决什么问题?

客服RPA&#xff0c;全称Robotic Process Automation&#xff0c;即机器人流程自动化。它是一种软件机器人&#xff0c;可以模拟人类在计算机上执行的任务&#xff0c;包括数据输入、网络浏览、电子邮件和文本消息发送等。 客服RPA适用于自动化处理客户咨询、投诉、退货等业务流…

wps左上角有绿色小三角的数字如何求和

1.这个状态是求和不了的&#xff0c;使用求和公式求出来的也是0 2.进行如下操作 3.转换好后 则可以求和成功了