欢迎光临散文网 会员登陆 & 注册

[思维提升|干货All in]6种算法解决LeetCode困难题:滑动窗口最大值

2023-03-14 20:55 作者:Erik_Tse  | 我要投稿

>视频讲解:https://www.bilibili.com/video/BV1db411f7M7


最近在leetcode遇到一道非常经典的题目:(https://leetcode.cn/problems/sliding-window-maximum/)

以前只会看题解用单调队列做,最近研究一下发现是一道很好的题,可以帮助我们提升“维护区间最值”的算法思维。

先介绍一下我解决这题所用的算法及其复杂度:

* 单调队列 $O(n)$

* st表 $O(nlogn)$

* 树状数组 $O(n(logn)^2)$

* 多重集合法$ O(nlogn)$

* 莫队$O (n sqrt{n})$

* 优先队列 $ O(nlogn) $

首先确定一点,单调队列是解决这道题最好的办法,但是其他的方法的适用范围更广。


1、单调队列

首先可以参考几篇优秀的文章:

算法学习笔记(66): 单调队列 - 知乎 (zhihu.com)

单调队列 - OI Wiki (oi-wiki.org)

我这里提几点值得注意的地方:

1.单调队列中存放的是下标,而不是元素值

2.单调队列是一个双端队列,尾插前先查队头后查队尾

3.单调队列维护的是元素值的单调性

有了这几点注意,代码就很好写了:

我做题习惯把输入数组存一个array,大家请勿介意。

2.st表

st表是一种基于DP(动态规划)思想的算法,也算是一种数据结构吧。

st表可以静态维护区间的最值,需要用的时间来预处理,后可以O(1)查询。

我们定义dp[i][j]表示

表示区间的最值,在这道题里我们认为是最大值(维护最小值同理)。看一下能不能开下,大概是maxn * 20的大小,可以开下。

通过定义不难发现,dp[i][0] = a[i],因为此时区间长度为1,那么最值就是元素a[i]本身。当j增大时,我们有转移方程(具体的原因可简单自行推导,以后的文章中也会有讲解):

dp[i][j] = max(dp[i][j - 1], dp[i + 2^(j-1)][j - 1])

查询十分方便,方法是从两边往中间尽可能多地覆盖。假设要查询的区间是[l,r]

,那么我们可以得到区间长度r - l + 1,现在求一个比长度小且最大的2的幂,然后把左右两块取大即可。

直接看代码:

3、树状数组

看见树状数组可能有小朋友会感到疑惑了哦,树状数组不是维护区间和的吗?怎么还来凑“区间最值”的热闹了。

其实树状数组不仅可以维护区间和,还可以“动态维护区间最值”,但是维护的方法和区间和略有不同。这一次主要看一下代码吧,具体的原理之后再讲。

树状数组节点t[k]维护的是区间[k - lowbit(k) + 1, k]。

树状数组主要突出的优点就是可以动态维护,但是注意在维护区间最值的时候仅可单点修改,不支持区间修改。

4、多重集合法

这种方法就十分简单粗暴了,就是维护一个不断移动的multiset,简直是暴力之王。

5、莫队

莫队需要基于multiset,在这道题里的优势并不明显,因为这题的询问都是有顺序的,但是可以写个莫队练个手。

莫队在处理随机区间查询问题的时候有独特的优势,因为足够暴力,所以维护的东西可以很多很杂,比如区间和,区间最值,区间元素种类数等。

以后我会详细讲莫队的,欢迎大家在右上角留下邮箱订阅我的博客(https://www.eriktse.com),每周更新优质的算法/技术/互联网文章!

6、优先队列

优先队列可以理解为一个“堆”结构。

我们知道优先队列可以维护最值,但是他只有一个堆顶怎么办呢?

我们只能保证堆顶是最大值但是却无法保证堆顶是在窗口内的呀!

为了解决这个问题,我们在每一次查询堆顶之前,都要对堆顶进行检查,直到堆顶在窗口内才能输出。

注意以下几点:

1.堆里存放的是下标,但是比较函数用值比较。

2.每次取出元素之前堆顶检查,只要堆顶的位置不在窗口内就一直弹出。

上代码!


[思维提升|干货All in]6种算法解决LeetCode困难题:滑动窗口最大值的评论 (共 条)

分享到微博请遵守国家法律