🧮 Algorithm Notebook
1、算法准备
1.1 牛客网编程OJ的典型输入输出
2、算法框架
2.1 动态规划
2.1.1 斐波那契数列
2.1.2 背包问题
2.1.3 贪心算法
2.1.4 序列和数组类问题
2.1.5 编辑距离
2.1.6 高楼扔鸡蛋
2.1.7 股票问题系列通解
2.1.8 最长有效括号
2.1.9 剪绳子
2.1.10 正则表达式匹配
2.2 二分查找
2.2.1 二分查找框架
2.2.2 搜索旋转排序数组
2.3 链表
2.3.1 反转链表
2.3.2 相交链表
2.3.3 合并链表
2.3.4 链表中倒数第k个节点
2.3.5 复制带随机指针的链表
2.4 排序算法
2.4.1 常见排序算法
2.5 二叉树
2.5.1 二叉树遍历
2.5.2 岛屿问题
2.5.3 二叉树路径问题
2.5.4 构造二叉树
2.6 回溯算法
2.6.1 回溯算法解题框架
2.6.2 N皇后
2.7 数组
2.7.1 删除有序数组中的重复项
2.7.2 滑动窗口最大值
2.7.3 调整数组顺序使奇数位于偶数前面
2.7.4 螺旋矩阵
2.7.5 多数元素
2.7.6 最大数
2.7.7 和为s的两个数字
2.7.8 构建乘积数组
2.7.9 两数之和
2.8 字符串
2.8.1 最小覆盖子串
2.8.2 比较版本号
2.8.3 验证IP地址
2.8.4 基本计算器 II
2.8.5 字符串解码
2.8.6 移掉 K 位数字
2.8.7 无重复字符的最长子串
2.8.8 第一个只出现一次的字符
2.8.9 翻转字符串里的单词
2.8.10 字符串转换整数 (atoi)
2.8.11 字符串四则运算
2.9 栈
2.9.1 最小栈
2.9.2 弹出序列
2.10 数学
2.10.1 用 Rand7() 实现 Rand10()
2.10.2 只出现一次的数字
2.10.3 整数反转
2.10.4 求1+2+…+n
2.10.5 二进制中1的个数
2.10.6 幂运算
2.10.7 1~n 整数中 1 出现的次数
2.10.8 数字序列中某一位的数字
2.10.9 丑数
2.10.10 n个骰子的点数
2.10.11 圆圈中最后剩下的数字
2.10.12 不用加减乘除做加法
2.10.13 x 的平方根
2.11 设计
2.11.1 LRU 缓存机制
-
+
tourist
register
Sign in
滑动窗口最大值
## 1 题目 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回滑动窗口中的最大值。 **示例 1:** ```txt 输入: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:** ```txt 输入:nums = [1], k = 1 输出:[1] ``` **示例 3:** ```txt 输入:nums = [1,-1], k = 1 输出:[1,-1] ``` **示例 4:** ```txt 输入:nums = [9,11], k = 2 输出:[11] ``` **示例 5:** ```txt 输入:nums = [4,-2], k = 2 输出:[4] ``` **提示:** * 1 <= nums.length <= 105 * -104 <= nums[i] <= 104 * 1 <= k <= nums.length ## 2 问题分析 1. 这道题不复杂,难点在于**如何在 $O(1)$ 时间算出每个【窗口】中的最大值**,**使得整个算法在线性时间完成**,**在一堆数字中**,**已知最值**,**如果给这堆数添加一个数**,**那么比较一下就可以很快算出最值**,**但如果减少一个数**,**就不一定能很快得到最值了**,**而要遍历所有数重新找最值**。 2. 回到这道题的场景,**每个窗口前进的时候**,**要添加一个数同时减少一个数**,**所以想在 $O(1)$ 的时间得出新的最值**,**就需要【单调队列】这种特殊的数据结构来辅助了**: 1. 整个单调队列的结构如下: ```java class MonotonicQueue { // 底层使用双端队列存储数据 private Deque<Integer> data = new ArrayDeque<>(); // 在队尾添加元素 n void push(int n); // 返回当前队列中的最大值 int max(); // 队头元素如果是 n,删除它 void pop(int n); } ``` 1. `push(int n)` 方法: 1. 这个方法中的 $n$**代表我们要添加的元素**。 2. 我们可以把加入数字的大小**类比于人的体重**,**把前面体重不足的都压扁了**,**直到遇到更大的量级才停住**。 3. **如果每个元素被加入时都这样操作**,**最终单调队列中的元素就会保持一个单调递减的顺序**,即**队首的元素最大**,**队尾的元素最小**。![无效的图片地址](/media/202111/2021-11-21_2239570.38904421963548796.png) 4. 该方法的具体实现为: ```java void push(int n) { while (data.size() != 0 && data.peekLast() < n) { data.pollLast(); } data.offerLast(n); } ``` 2. `max()` 方法: 1. **由于每次添加元素时会把小于添加元素的元素弹出**,**因此每次添加元素后的队列都是一个单调递减队列**,**队首的元素即为队列中的最大元素**,**直接返回即可**。 2. 该方法的具体实现为: ```java int max() { return data.peekFirst(); } ``` 3. `pop(int n)`: 1. 这个方法中的 $n$**代表我们要弹出的元素**。 2. **之所以要判断 `data.peekFirst() == n`**,**是因为我们想删除的对头元素 $n$ 可能已经被【压扁】了**,**这时候就不用删除了**。![无效的图片地址](/media/202111/2021-11-21_2246440.5904891752502391.png) ## 3 参考代码 ```java /** * 自定义单调递增队列 */ class MonotonicQueue { // 底层使用双端队列存储数据 private Deque<Integer> data = new ArrayDeque<>(); /** * 插入数据 * @param n 插入的数据 */ void push(int n) { while (data.size() != 0 && data.peekLast() < n) { // 如果队尾的元素比插入的数据小,则直接将队尾的元素弹出 data.pollLast(); } // 将元素插入队尾 data.offerLast(n); } /** * 返回队列中的最大值 * @return 队列中的最大值 */ int max() { // 由于在插入元素的时候保证双端队列是递增的,因此队首元素即为双端队列中的最大元素 return data.peekFirst(); } /** * 弹出元素 * @param n 弹出的元素 */ void pop(int n) { // 因为我们想删除的对头元素可能已经在其他元素插入的时候被弹出了,这时候就不用删除了 if (data.size() != 0 && data.peekFirst() == n) { data.pollFirst(); } } } /** * 239. 滑动窗口最大值 * @param nums 整数数组 * @param k 滑动窗口大小 * @return 滑动窗口中的最大值 */ public int[] maxSlidingWindow(int[] nums, int k) { MonotonicQueue window = new MonotonicQueue(); int[] res = new int[nums.length - k + 1]; int index = 0; for (int i = 0; i < nums.length; i++) { if (i < k - 1) { // 先填满窗口的前 k - 1 window.push(nums[i]); } else { // 窗口向前滑动 window.push(nums[i]); res[index++] = window.max(); window.pop(nums[i - k + 1]); } } return res; } ``` ## 参考文献 1. [239. 滑动窗口最大值](https://leetcode-cn.com/problems/sliding-window-maximum)。 2. [单调队列解题详解](https://leetcode-cn.com/problems/sliding-window-maximum/solution/dan-diao-dui-lie-by-labuladong)。
ricear
June 1, 2022, 11 a.m.
©
BY-NC-ND(4.0)
转发文档
Collection documents
Last
Next
手机扫码
Copy link
手机扫一扫转发分享
Copy link
Markdown文件
share
link
type
password
Update password