剑指offer第六十四题滑动窗口的最大值

 

题目描述(这个题考察单调队列)

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

窗口大于数组长度的时候,返回空

方法一:

小知识:队列左边头,右边尾。(我觉得是对的)

deque定义:

双向队列:支持插入删除元素的线性集合

特性:

  1. 插入、删除、获取操作支持两种形式:快速失败和返回nulltrue/false
  2. 既具有FIFO特点又具有LIFO特点,即是队列又是栈
  3. 不推荐插入null元素,null作为特定返回值表示队列为空
  4. 未定义基于元素相等的equals和hashCode

插入元素

  • addFirst(): 向队头插入元素,如果元素为空,则发生NPE

  • addLast(): 向队尾插入元素,如果为空,则发生NPE

  • offerFirst(): 向队头插入元素,如果插入成功返回true,否则返回false

  • offerLast(): 向队尾插入元素,如果插入成功返回true,否则返回false

移除元素

  • removeFirst(): 返回并移除队头元素,如果该元素是null,则发生NoSuchElementException

  • removeLast(): 返回并移除队尾元素,如果该元素是null,则发生NoSuchElementException

  • pollFirst(): 返回并移除队头元素,如果队列无元素,则返回null

  • pollLast(): 返回并移除队尾元素,如果队列无元素,则返回null

获取元素

  • getFirst(): 获取队头元素但不移除,如果队列无元素,则发生NoSuchElementException

  • getLast(): 获取队尾元素但不移除,如果队列无元素,则发生NoSuchElementException

  • peekFirst(): 获取队头元素但不移除,如果队列无元素,则返回null

  • peekLast(): 获取队尾元素但不移除,如果队列无元素,则返回null

栈操作

pop(): 弹出栈中元素,也就是返回并移除队头元素,等价于removeFirst(),如果队列无元素,则发生NoSuchElementException

push(): 向栈中压入元素,也就是向队头增加元素,等价于addFirst(),如果元素为null,则发生NPE,如果栈空间受到限制,则发生IllegalStateException

写这个:

import java.util.*;
class Solution {
    public ArrayList<Integer> maxInWindows(int[] nums, int k) {
        if (nums.length == 0 || k == 0 || k > nums.length)
            return new ArrayList<>();
        Deque<Integer> deque = new LinkedList<>();
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < k; i++) { // 未形成窗口
            while (!deque.isEmpty() && deque.peekLast() < nums[i])//队列左边头,右边尾
                deque.removeLast();
            deque.addLast(nums[i]);
        }
        list.add(deque.peekFirst());
        for (int i = k; i < nums.length; i++) { // 形成窗口后
            if (deque.peekFirst() == nums[i - k])//如果相等,说明当前单调队列的第一个数就是当前窗口的最左边的数,把第一个数移掉,窗口向后移动
                deque.removeFirst();//如果不相等,就说明滑动窗口的最大值是窗口中的后两个数中的一个数
            while (!deque.isEmpty() && deque.peekLast() < nums[i])
                deque.removeLast();
            deque.addLast(nums[i]);
            list.add(deque.peekFirst());
        }
        return list;
    }
}

public class Main {
    public static void main(String[] args) {
        int[] nums = {8,7,6,5,4,3,2,1};
        int size = 3;
        Solution p = new Solution();
        ArrayList<Integer> res = p.maxInWindows(nums, size);
        for (int i = 0; i < res.size(); i++) {
            System.out.print(res.get(i) + " ");
        }
    }
}

 

下面的就稍微乱点了

package test;
import java.util.ArrayDeque;
import java.util.ArrayList;
class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
        ArrayList<Integer> res = new ArrayList<>();
        if(size == 0) return res;
        int begin;
        ArrayDeque<Integer> q = new ArrayDeque<>();
        for(int i = 0; i < num.length; i++){
            begin = i - size + 1;
            if(q.isEmpty())//q是用来存i的,左边是最大值
                q.add(i);
            else if(begin > q.peekFirst())//begin超过前面的最大值了
                q.pollFirst();

            while((!q.isEmpty()) && num[q.peekLast()] <= num[i])//因为下面执行完q.pollLast()之后,
//q可能就是空了;还有一个重点:这里的<=的=一定是要的,因为i要往前走
                q.pollLast();
            q.add(i);
            if(begin >= 0)
                res.add(num[q.peekFirst()]);
        }
        return res;
    }
}
public class Main {
    public static void main(String[] args) {
        int[] temp = {2,3,4,2,6,2,5,1};
        Solution p = new Solution();
        ArrayList<Integer> b = p.maxInWindows(temp,3);
        System.out.println("输出中位数"+ b);
    }
}

 简化版:

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums == null || nums.length < 2) return nums;
        // 双向队列 保存当前窗口最大值的数组位置 保证队列中数组位置的数值按从大到小排序
        LinkedList<Integer> queue = new LinkedList();
        // 结果数组
        int[] result = new int[nums.length-k+1];
        // 遍历nums数组
        for(int i = 0;i < nums.length;i++){
            // 保证从大到小 如果前面数小则需要依次弹出,直至满足要求
            while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]){
                queue.pollLast();
            }
            // 添加当前值对应的数组下标
            queue.addLast(i);
            // 判断当前队列中队首的值是否有效
            if(queue.peek() <= i-k){
                queue.poll();   
            } 
            // 当窗口长度为k时 保存当前窗口中最大值
            if(i+1 >= k){
                result[i+1-k] = nums[queue.peek()];
            }
        }
        return result;
    }
}

方法二:冒泡排序,找滑动窗口的最大值。

package test;
import java.util.ArrayList;
class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size){
        ArrayList<Integer> list = new ArrayList<>();
        //边界条件
        if (num == null || size <= 0 || size > num.length) {
            return list;
        }
        for (int i = 0; i <= num.length - size; i++) {
            int max = num[i];
            for (int j = i + 1; j <= i + size - 1; j++) {
                if (num[j] > max) {
                    max = num[j];
                }
            }
            list.add(max);
        }
        return list;
    }
}

public class Main {
    public static void main(String[] args) {
        int[] temp = {2,3,4,2,6,2,5,1};
        Solution p = new Solution();
        ArrayList<Integer> b = p.maxInWindows(temp,3);
        System.out.println("输出中位数"+b);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值