滑動窗口的最大值-雙端隊列

題目描述

給定一個數組 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

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

算法思路

可以使用雙向隊列解決此問題。首先初始化一個空的隊列,遍歷數組元素。如果當前遍歷到的數字大於隊列末尾的數字,則將末尾的這個數字移除隊列,如果這個時候新的隊尾數字仍然小於當前讀入的數字,則繼續執行移除操作,直到隊列爲空,或者隊尾數字大於當前讀到的數字。一直這樣的操作,可以保證隊列的第一個數字始終是當前隊列裏最大的,隊列數字倒序排列。
這個時候還存在一個問題,隊列中可能會存儲一些不是本窗口內的數字,所以需要一直對隊列內的數字進行維護。每次遍歷到數組中新的數字時,都將窗口中最左邊的數字從隊列中移除,因爲窗口再滑動之後這個數字就不是窗口中的數字了,或者說過期了。因爲前面比較大小時很多數都已經被移除了,所以我們無法判斷窗口中最左邊的數字是否就是隊列頭的數字。
爲了解決這個問題,我們在隊列中存儲數組元素的下標而不是這個數本身。這樣,每次滑動窗口之前,都判斷隊列頭的下標是否爲窗口最左邊數字的下標,如果是,則從隊列中移除。
當遍歷的次數大於等於窗口大小時,就需要獲取每個窗口中的最大值了,這個最大值就是隊列頭這個下標在原數組中的數字。
整個算法只需要遍歷一次數組,對隊列的操作複雜度是O(1),算法複雜度爲O(n)。

GitHub上的動態圖可以幫助理解

代碼實現

public int[] maxSlidingWindow(int[] nums, int k) {
        // 如果數組爲空,返回[]
        if (nums.length == 0) {
            return new int[0];
        }
        int[] res = new int[nums.length - k + 1];
        // 定義一個雙端隊列
        Deque<Integer> deque = new LinkedList<>();
        // 遍歷數組
        for (int i = 0; i < nums.length; i++) {
            // 如果隊列不爲空且當前讀入的數字大於隊列的最後一個數字
            while (!deque.isEmpty() && nums[i] > nums[deque.getLast()]) {
                deque.removeLast();
            }
            // 將當前元素的下標加到隊列尾部
            deque.addLast(i);
            // 窗口內數字已滿,該窗口內的最大值即爲隊列頭
            if (i >= k - 1) {
                res[i - k + 1] = nums[deque.getFirst()];
            }
            // 當前隊列頭的下標是否爲窗口最左邊數字的下標
            // 在移動窗口前將其從隊列移除
            if (deque.getFirst() == i - k + 1) {
                deque.removeFirst();
            }
        }
        return res;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章