leetcode【栈与队列—困难】 239.滑动窗口

简介: leetcode【栈与队列—困难】 239.滑动窗口

题目


题目来源leetcode


leetcode地址:239. 滑动窗口最大值,难度:困难。


题目描述(摘自leetcode):


给你一个整数数组 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]
示例 3:
输入:nums = [1,-1], k = 1
输出:[1,-1]
示例 4:
输入:nums = [9,11], k = 2
输出:[11]
示例 5:
输入:nums = [4,-2], k = 2
输出:[4]
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length


本地调试代码:


class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        ...
    }
    public static void main(String[] args) {
        System.out.println(Arrays.toString(new Solution().maxSlidingWindow(new int[]{1,3,-1,-3,5,3,6,7},3)));
    }
}



题解


NO1:自定义队列


思路: 自定义一种队列规则,将入队元素依次从队列后往前比对,若是有小于的直接出队直到碰到大于的时候停止,接着再入队,此时队列队头一直会是最大值。


示例:1,3,-1,-3,5,3,6,7  k=3
队列 deque=[],左边是队头 下面=>指的是拿到队头的元素值,并没有直接出队
① [1,3,-1],-3,5,3,6,7  首先先将第一组的元素入队,最终队列为[3]   => 3  
②  1,[3,-1,-3],5,3,6,7  将1出队(队头若是1就将1出队,实际无),接着入队-3,-3与队列从后往前比,最终队列为[3,-3] => 3
③  1,3,[-1,-3,5],3,6,7  将3出队(队头若是3就将3出队。实际有),接着入队5,5与队列往后往前比,最终队列为[5] => 5
④  1,3,-1,[-3,5,3],6,7  将-1出队(队头若是-1就将-1出队。实际无),接着入队3,3与队列往后往前比,最终队列为[5,3] => 5
⑤  1,3,-1,-3,[5,3,6],7  将6出队(队头若是-3就将-3出队。实际无),接着入队6,6与队列往后往前比,最终队列为[6] => 6
⑥  1,3,-1,-3,5,[3,6,7]  将5出队(队头若是5就将5出队。实际无),接着入队7,7与队列往后往前比,最终队列为[7] => 7
最终每组值为[3,3,5,5,6,7]


代码:


class MyQueue{
    private Deque<Integer> deque;
    public MyQueue(){
        deque = new LinkedList<>();
    }
    //添加元素时,将队列从后往前比num小的出列
    public void add(int num){
        while(!deque.isEmpty() && deque.getLast() < num){
            deque.pollLast();
        }
        deque.addLast(num);
    }
    //队头元素若是与num相同则出队
    public void pop(int num){
        if(!deque.isEmpty() && num == deque.getFirst()){
            deque.pollFirst();
        }
    }
    //获取队头元素(最大的值)
    public int peek(){
        return deque.getFirst();
    }
}
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        MyQueue myQueue = new MyQueue();
        int[] maxNums = new int[nums.length-k+1];
        //先将第一组的值放入,并且获取最大的值
        for (int i = 0; i < k; i++) {
            myQueue.add(nums[i]);
        }
        maxNums[0] = myQueue.peek();
        for (int i = 1; i < maxNums.length; i++) {
            //进来先去除不在范围内的元素
            myQueue.pop(nums[i-1]);
            //添加新的元素
            myQueue.add(nums[i+k-1]);
            //获取出当前队列中最大的元素
            maxNums[i] = myQueue.peek();
        }
        return maxNums;
    }
}


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LqPo2Auu-1636733226055)(C:\Users\93997\AppData\Roaming\Typora\typora-user-images\image-20211103200737819.png)]



NO2:窗口内比较取值


思路:不使用某种数据结构,使用max、pos来临时存储最大值,max为最大值,pos为最大值索引。首先进行一轮比较得出对应的最大值,接着通过判断pos是否在下一次滑动窗口范围来进行相应的比较操作。


这里有一点不太清楚的是仅仅通过滑动窗口最左、最优右临界值来与最大值进行比较就能够让时间效率优化那么多?若是k为很大情况,那么临界最左-最右中间这些还是要一个一个进行比较,难道没有消耗特别多时间吗?(该题解与我最初思路一致,只不过没有想到的是多增加了下面第15行临界最左值判断就能优化时间效率那么多)

代码:


public int[] maxSlidingWindow(int[] nums, int k) {
    int[] maxNums = new int[nums.length - k + 1];
    int pos = -1;
    int max = Integer.MIN_VALUE;
    for (int i = 0,winEndPos = k-1; i < maxNums.length; i++,winEndPos++) {
        if(pos >= i){
            if(nums[winEndPos] >= max){
                max = nums[winEndPos];
                pos = winEndPos;
            }
            //下面nums[winEndPos]、nums[i]相当于窗口的最右侧与最左侧
        }else if(nums[winEndPos] >= max-1){  //相当于>max,这里>=max-1,实际上是为了处理max = Integer.MIN_VALUE情况,-1由于超过原始长度就为最大值
            max = nums[winEndPos];
            pos = winEndPos;
        }else if(nums[i] >= max-1){
            max = nums[i];
            pos = i;
        }else{
            //若是上面两个情况不过,那么就需要进行遍历重新比出最大值
            max = nums[i];
            for (int j = i+1; j < i + k; j++) {
                if (nums[j] > max) {
                    max = nums[j];
                    pos = j;
                }
            }
        }
        maxNums[i] = max;
    }
    return maxNums;
}


相关文章
|
13天前
|
存储 算法 Python
二刷力扣--栈和队列
二刷力扣--栈和队列
|
14天前
|
算法
【LeetCode刷题】滑动窗口解决问题:串联所有单词的子串(困难)、最小覆盖子串(困难)
【LeetCode刷题】滑动窗口解决问题:串联所有单词的子串(困难)、最小覆盖子串(困难)
|
14天前
|
算法 容器
【LeetCode刷题】滑动窗口解决问题:水果成篮、找到字符串中所有字母异位词
【LeetCode刷题】滑动窗口解决问题:水果成篮、找到字符串中所有字母异位词
|
17天前
|
存储 算法 数据可视化
力扣155题最全解法:如何实现支持常数时间获取最小值的最小栈(附详细图解和复杂度分析)
力扣155题最全解法:如何实现支持常数时间获取最小值的最小栈(附详细图解和复杂度分析)
|
14天前
【LeetCode刷题】滑动窗口思想解决:最大连续1的个数 III、将x减到0的最小操作数
【LeetCode刷题】滑动窗口思想解决:最大连续1的个数 III、将x减到0的最小操作数
|
14天前
【LeetCode刷题】滑动窗口思想解决问题:长度最小的子数组、无重复字符的最长子串
【LeetCode刷题】滑动窗口思想解决问题:长度最小的子数组、无重复字符的最长子串
|
14天前
|
容器
【LeetCode刷题】栈和队列题目练习~
【LeetCode刷题】栈和队列题目练习~
|
14天前
|
算法
【经典LeetCode算法题目专栏分类】【第8期】滑动窗口:最小覆盖子串、字符串排列、找所有字母异位词、 最长无重复子串
【经典LeetCode算法题目专栏分类】【第8期】滑动窗口:最小覆盖子串、字符串排列、找所有字母异位词、 最长无重复子串
|
17天前
|
存储 算法 数据可视化
深入解析力扣159题:至多包含两个不同字符的最长子串(滑动窗口法详细图解)
深入解析力扣159题:至多包含两个不同字符的最长子串(滑动窗口法详细图解)
|
17天前
|
存储 SQL 算法
LeetCode题目100:递归、迭代、dfs使用栈多种算法图解相同的树
LeetCode题目100:递归、迭代、dfs使用栈多种算法图解相同的树