239. 滑动窗口最大值-单调栈

题目链接:https://leetcode-cn.com/problems/sliding-window-maximum/

给你一个整数数组 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

输出:
返回结果是一个数组,每个元素代表各个窗口的最大值。一共有n-k+1个窗口,结果数组的长度为n-k+1

思路:
使用单调栈,栈底的值代表窗口最大值。

本题要点在于保证单调栈底元素永远是当前队列的最大值,这样便于取第一个元素放到结果集。

思考两个问题:

1 怎么取出窗口的最大值?
答:栈底元素就是最大值

3 什么时候可以取出窗口最大值放到结果集中?
答:下标 i 比窗口 k 大之后

代码

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {

        int[] result = new int[nums.length-k+1];
        // 用最常见的List来表示窗口(双端队列)
        ArrayList<Integer> window = new ArrayList<>();

        for (int i = 0 ; i < nums.length; i++) {

            while (!window.isEmpty() && nums[window.get(window.size() - 1)] <= nums[i] ) {
                window.remove(window.size() - 1);
            }    

            window.add(i);

            // 窗口大小是3,需要把已经小于范围的下标去掉
            if (window.get(0) <= i - k) {
                window.remove(0);
            }

            // 什么时候可以把结果放到结果集?答:下标比窗口大的时候
            if (i + 1 >= k) {
                // 放到哪个位置?答:每次遍历都要放,顺序放到结果集的位置
                result[i + 1 - k] = nums[window.get(0)];
            }


        }
        return result;
    }
}

用链表实现单调栈,随机读写性能较好

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int[] result = new int[nums.length - k + 1];

        LinkedList<Integer> queue = new LinkedList<>();

        for (int i = 0; i < nums.length; i++) {

            // 添加元素之前做检验: 把队列中小于nums[i]的值去掉
            while (!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]) {
                queue.pollLast();
            }

            // 记录当前元素的下标
            queue.add(i);

            // 剔除队列左侧超出窗口范围的下标
            if (queue.peekFirst() <= i - k) {
                queue.poll();
            }
            // 窗口等于k,保存队列最大值
            if (i + 1 >= k) {
                result[i - k + 1] = nums[queue.peek()];
            }

        }

        return result;
    }
}

推荐题解:
https://leetcode-cn.com/problems/sliding-window-maximum/solution/shuang-xiang-dui-lie-jie-jue-hua-dong-chuang-kou-2/

在Python中,单调栈和单调队列是两种不同的数据结构。单调栈是一个栈,它的特点是栈内的元素是单调的,可以是递增或递减的。在构建单调栈时,元素的插入和弹出都是在栈的一端进行的。与此类似,单调队列也是一个队列,它的特点是队列内的元素是单调的,可以是递增或递减的。在构建单调队列时,元素的插入是在队列的一端进行的,而弹出则是选择队列头进行的。 单调队列在解决某些问题时,能够提升效率。例如,滑动窗口最大值问题可以通过使用单调队列来解决。单调队列的结构可以通过以下代码来实现: ```python class MQueue: def __init__(self): self.queue = [] def push(self, value): while self.queue and self.queue[-1 < value: self.queue.pop(-1) self.queue.append(value) def pop(self): if self.queue: return self.queue.pop(0) ``` 上述代码定义了一个名为MQueue的类,它包含一个列表作为队列的存储结构。该类有两个方法,push和pop。push方法用于向队列中插入元素,它会删除队列尾部小于插入元素的所有元素,并将插入元素添加到队列尾部。pop方法用于弹出队列的头部元素。 总结来说,单调栈和单调队列都是为了解决特定问题而设计的数据结构。单调栈在构建时元素的插入和弹出都是在栈的一端进行的,而单调队列则是在队列的一端进行的。在Python中,可以通过自定义类来实现单调队列的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值