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,3,0,2,4]
输出:0
解释:
nums 无论怎么变化总是有 3 分。
所以我们将选择最小的 k,即 0。

提示

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104
  • 1 <= k <= nums.length

解题思路:

方法一:单调队列

单调队列:队列中的元素是单调递增或单调递减的队列就是单调队列。


  1. 维护队列中的元素递增

对于这道题需要维护一个单调递增的队列。
在这里插入图片描述

由队尾到队头 元素是单调递增的。如果要入队元素大于队尾元素,则队尾元素出队,这是个循环操作,如下:

while(deque.peekLast() < newElement){
	deque.pollLast();
}
deque.push(newElement); // 新元素入队

  1. 如何保证队列中的元素是窗口内的,因为窗口一直在向右移动?

在这里插入图片描述

初始时,窗口为1,队列中元素为9, 8, 7。窗口向右移动时(窗口2)发现9已不是窗口中的元素,但9依然在队列中,且9为队列的队头元素,需要将9从队列的队头弹出。所以需要进行如下判断:

if(deque.peekFirst() == v){ // v为上一个窗口最左边的值。
    deque.poolFirst();
}

整体代码如下:

public int[] maxSlidingWindow(int[] nums, int k) {
    int n = nums.length;
    int[] ans = new int[n - k + 1];
    Deque<Integer> deque = new LinkedList<>();
    int idx = 0;
    for(int i = 0; i < nums.length; i++){

        // 保证队列中的元素是窗口内的
        if(!deque.isEmpty() && i - k>= 0 && nums[i - k] == deque.peekFirst()){
            deque.pollFirst();
        }
        // 维护队列中的元素是递增的
        while (!deque.isEmpty() && nums[i] > deque.peekLast()){
            deque.pollLast();
        }
        deque.addLast(nums[i]);
        if(i>= k - 1){
            ans[idx++] = deque.peekFirst();
        }
    }
    return ans;
}

方法二:线段树

百度百科:线段树


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fEboTtnz-1690452411315)(D:\ProgramFiles\Typora\typora-images\image-20230727175053720.png)]

线段树中的每个节点存储的是这个区间的最大值。

整体代码如下:

public int[] maxSlidingWindow(int[] nums, int k) {
    int len = nums.length;
    int[] ans = new int[len - k + 1];
    this.tr = new Node[len * 4+1];
    build(1,1,len);
    for(int i = 0; i<len;i++){
        update(1,i+1,nums[i]);
    }
    for(int i = 0; i< len -k+1;i++){
        ans[i] = query(1,i+1,i+k);
    }
    return ans;
}

class Node {
    int l, r, v;
    Node(int l, int r) {
        this.l = l;
        this.r = r;
        v = Integer.MIN_VALUE;
    }
}

Node[] tr;

void pushup(int u) {
    tr[u].v = Math.max(tr[u << 1].v, tr[u << 1 | 1].v);
}

void build(int u, int l, int r) {
    tr[u] = new Node(l, r);
    if (l != r) {
        int mid = tr[u].l+tr[u].r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
    }
}

void update(int u, int x, int v){
    if(x== tr[u].l&&tr[u].r==x){
        tr[u].v = Math.max(tr[u].v, v);
    } else{
        int mid = tr[u].l+tr[u].r>>1;
        if(x<=mid){
            update(u<<1,x,v);
        } else{
            update(u<<1|1,x,v);
        }
        pushup(u);
    }
}
int query(int u, int l, int r){
    if(l<= tr[u].l&&tr[u].r<=r){
        return tr[u].v;
    } else{
        int mid = tr[u].l+tr[u].r>>1;
        int ans = Integer.MIN_VALUE;
        if(l<=mid){
            ans =query(u<<1,l,r);
        } 
        if(r>mid){
            ans = Math.max(query(u<<1|1,l,r),ans);
        }
        return ans;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值