zoukankan      html  css  js  c++  java
  • Hard | LeetCode 239 | 剑指 Offer 59

    剑指 Offer 59 - I. 滑动窗口的最大值

    给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

    示例:

    输入: 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
    

    提示:

    你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。

    解题思路

    使用一个辅助队列存储遍历过程可能成为最大值的数字, 并且如果辅助队列的队头如果是当前滑动窗口的最左边元素, 从而需要将队首元素删除。所以在队列里存储元素的下标更加方便于移除元素。

    下面详细讲讲在遍历数组并入队的过程, 首先需要将入队元素与队尾元素比较, 如果小于队尾元素, 从当前已经遍历过的数字来看, 当前元素是有可能成为队列最大值的, 因为左边的更大的元素是有可能被移除队列的, 待它被移除队列之后, 当前元素自然而然就是最大的。

    如果当前入队元素大于队尾元素, 则要将队尾元素依次出队, 直到队尾元素大于当前入队元素, 因为队列中现存的所有元素都和当前元素在一个窗口内(不在窗口内的元素会被移除), 所以左边的比当前小的元素是不可能成为队列最大值的, 所以将其移除。

    综上, 使用一个单调递减的队列就可以解决上述问题。

    // 直接写一个单调队列的类
    class MonotonicQuene {
        
       	private LinkedList<Integer> queue;
    
        public MonotonicQuene() {
            queue = new LinkedList<>();
        }
    
        public void push(int element) {
            while (!queue.isEmpty() && element > queue.getLast()) {
                queue.removeLast();
            }
            queue.addLast(element);
        }
    
        public void pop() {
            queue.removeLast();
        }
    
        public int max() {
            return queue.getFirst();
        }
    }
    
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        MonotonicQuene mq = new MonotonicQuene();
        int[] res = new int[n - k + 1];
        int resIndex = 0;
        for (int i = 0; i < nums.length; i++) {
            mq.push(nums[i]);
            // i-k+1 代表滑动窗口的最左边的第一个元素
            if (i - k + 1 >= 0) {
                // 每次将队列最大值添加进res里
                res[resIndex++] = mq.max();
                
                // 窗口最左边元素是队列最大值 说明队列需要移除这个元素
                // 如果不是队列最大值 那就不需要pop, 因为它已经被pop了。
                if (nums[i-k+1] == mq.max()) {
                    mq.pop();
                }
            }
        }
        return res;
    }
    

    上述代码专门写了一个单调递减队列的数据结构, 下面是没有写这个结构, 但是操作相同的代码

    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return new int[0];
        }
        int[] res = new int[nums.length - k + 1];
        int cursor = 0;
        Deque<Integer> queue = new ArrayDeque<>();
        for(int i = 0; i < nums.length; i++) {
            if (i < k-1) {
                add(queue, nums, i);
            } else {
                add(queue, nums, i);
                res[cursor++] = nums[queue.peek()];
                // 如果当前的队列的最大值是窗口的最左边, 需要手动将其移除
                if (queue.peek() == i - k + 1) {
                    queue.poll();
                }
            }
        }
        return res;
    }
    // 向单调递减队列添加元素
    public void add(Deque<Integer> queue, int[] nums, int index) {
        while(!queue.isEmpty()) {
            int tailIdx = queue.getLast();
            // 把队尾的比当前入队元素小的元素全部出队
            if (nums[index] > nums[tailIdx]) {
                queue.pollLast();
            } else {
                break;
            }
        }
        // 然后把当前元素加进队尾
        queue.add(index);
    }
    
  • 相关阅读:
    HDU 2955 Robberies(01背包)
    HDU 2602 Bone Collector(01背包)
    HUST 1352 Repetitions of Substrings(字符串)
    HUST 1358 Uiwurerirexb jeqvad(模拟解密)
    HUST 1404 Hamming Distance(字符串)
    HDU 4520 小Q系列故事――最佳裁判(STL)
    HDU 2058 The sum problem(枚举)
    【破解】修改程序版权、添加弹窗
    HDU 1407 测试你是否和LTC水平一样高(枚举)
    HDU 1050 Moving Tables(贪心)
  • 原文地址:https://www.cnblogs.com/chenrj97/p/14282086.html
Copyright © 2011-2022 走看看