滑動(dòng)窗口最大值

滑動(dòng)窗口最大值

描述

給定一個(gè)數(shù)組 nums,有一個(gè)大小為 k 的滑動(dòng)窗口從數(shù)組的最左側(cè)移動(dòng)到數(shù)組的最右側(cè)。
你只可以看到在滑動(dòng)窗口內(nèi)的 k 個(gè)數(shù)字?;瑒?dòng)窗口每次只向右移動(dòng)一位。

返回滑動(dòng)窗口中的最大值。

示例

輸入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
輸出: [3,3,5,5,6,7]
解釋:

滑動(dòng)窗口的位置 最大值


[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

來(lái)源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/sliding-window-maximum

題解

暴力破解

思路
遍歷每個(gè)滑動(dòng)窗口,在每個(gè)滑動(dòng)窗口中獲取最大值;
代碼

  public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        if (n * k == 0) return new int[0];

        int [] output = new int[n - k + 1];
        for (int i = 0; i < n - k + 1; i++) {
            int max = Integer.MIN_VALUE;
            for(int j = i; j < i + k; j++)
                max = Math.max(max, nums[j]);
            output[i] = max;
        }
        return output;
    }

復(fù)雜度分析
時(shí)間復(fù)雜度: 在nums數(shù)組中遍歷獲得滑動(dòng)窗口O(N),在滑動(dòng)窗口中獲取最大值的時(shí)間復(fù)雜度為O(k),
因?yàn)槊恳淮伪闅v數(shù)組獲取滑動(dòng)窗口都需獲取滑動(dòng)窗口的最大值,
因此最終的時(shí)間復(fù)雜度為O(N*k);
空間復(fù)雜度:返回值所需O(N-l+1);

優(yōu)先隊(duì)列

思路
使用大頂堆來(lái)維護(hù)滑動(dòng)窗口中的的數(shù)據(jù),大頂堆的堆頂元素即為滑動(dòng)窗口中最大值。
算法步驟:

  1. 準(zhǔn)備:
  • 源數(shù)據(jù)nums數(shù)組,長(zhǎng)度為N
  • 滑動(dòng)窗口大小k
  • 優(yōu)先隊(duì)列(大頂堆)priorityQueue,大小需為k
  • 結(jié)果數(shù)組results,長(zhǎng)度為N-k+1
  1. 初始化priorityQueue:由于滑動(dòng)窗口是從nums的下標(biāo)k-1開(kāi)始遍歷的,因此先
    使用nums中[0,k-1]上的值來(lái)初始化priorityQueue;
    nums[0,k-1]為第一個(gè)滑動(dòng)窗口,因此results[0]為此時(shí)priorityQueue的最優(yōu)先元素;
  2. 遍歷nums:從k到N遍歷nums,下標(biāo)為i,此時(shí)滑動(dòng)窗口范圍為nums[i-k+1,i],priorityQueue
    的值為nums[i-k,i-1];
  3. 調(diào)整優(yōu)先隊(duì)列:將nums[i]添加到priorityQueue,nums[i-k],從priorityQueue移除,
    保持priorityQueue的值與滑動(dòng)窗口的值一致;
  4. 添加滑動(dòng)窗口最大值:優(yōu)先隊(duì)列的隊(duì)首元素為滑動(dòng)窗口此時(shí)的最大值,
    results[i-k+1]=priorityQueue的隊(duì)首元素;
    代碼
public class PriorityQueueSolution {
    Comparator<Integer> comparator = new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
           return o2-o1;
        }
    };

    public int[] maxSlidingWindow(int[] nums, int k) {
        int len = nums.length;
        if (k > len || k < 1) {
            return new int[0];
        }
        int[] result = new int[len - k + 1];
        PriorityQueue<Integer> queue = new PriorityQueue<>(k, comparator);
        //初始化優(yōu)先隊(duì)列
        int i = 0;
        while (i < k - 1) {
            queue.add(nums[i]);
            i++;
        }
        //從滑動(dòng)窗口中獲取最大值
        for (int right = k - 1; right < len; right++) {
            queue.add(nums[right]);
            if (queue.size() > k) {
                queue.remove(nums[right-k]);
            }
            result[right - k + 1] = queue.element();
        }
        return result;
    }
}

復(fù)雜度分析
時(shí)間復(fù)雜度: 在nums數(shù)組中遍歷獲得滑動(dòng)窗口O(N),優(yōu)先隊(duì)列刪除、添加元素的復(fù)雜度為O(logk),
獲取優(yōu)先隊(duì)列的隊(duì)首元素的時(shí)間復(fù)雜度為O(1),
因此最終的時(shí)間復(fù)雜度為O(N*logk);
空間復(fù)雜度:返回值所需O(N-l+1),優(yōu)先隊(duì)列為O(k),最終為O(N);

雙端隊(duì)列

思路
使用雙端隊(duì)列來(lái)存儲(chǔ)滑動(dòng)窗口在nums中對(duì)應(yīng)的下標(biāo);
雙端隊(duì)列限制:

  • 靠近尾部的隊(duì)列元素的值需要比靠近隊(duì)首的元素的值要大,
    即當(dāng)窗口滑動(dòng)時(shí),滑動(dòng)窗口中增加的元素在nums的下標(biāo)是從隊(duì)尾添加的,
    滑動(dòng)窗口移除的元素在nums中的下標(biāo)是從隊(duì)首移除的;
  • 靠近尾部的隊(duì)列元素的值作為下標(biāo)在nums值需要比靠近隊(duì)首的要小,
    即當(dāng)隊(duì)列添加下標(biāo)之前要從隊(duì)列隊(duì)尾去除這些下標(biāo)(它們?cè)趎ums中對(duì)應(yīng)的值要比要添加
    下標(biāo)在nums中的要小);
    經(jīng)過(guò)上述限制,隊(duì)列的隊(duì)首下標(biāo)在nums中對(duì)應(yīng)的值始終是隊(duì)列的最大值,即為滑動(dòng)窗口中的最大值;
    代碼
public class DequeSolution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int len = nums.length;
        if (k > len || k < 1) {
            return new int[0];
        }
        int[] result = new int[len - k + 1];
        //用于存儲(chǔ)在滑動(dòng)窗口中有效的索引,且雙端隊(duì)列會(huì)從前向后維護(hù)一個(gè)降序的排列,
        // 獲取滑動(dòng)窗口中的最大值,只需要獲取雙端隊(duì)列的首值即可
        Deque<Integer> indexDeque = new ArrayDeque<>(k);
        for (int i = 0; i < len; i++) {
            addAndRemove(nums, indexDeque, i, k);
            if (i >= k - 1) {
                result[i - k + 1] = nums[indexDeque.getFirst()];
            }
        }
        return result;
    }

    private void addAndRemove(int[] nums, Deque<Integer> indexDeque, int index, int k) {
        //如果indexDeque中的開(kāi)始元素的索引在當(dāng)前滑動(dòng)窗口的范圍之外刪除
        if (indexDeque != null && indexDeque.size() > 0 && (index - indexDeque.getFirst()) > k - 1) {
            indexDeque.removeFirst();
        }
        //循環(huán)從indexDeque后部取出索引indexLast,與要添加的索引index,將它們?cè)趎ums中的值比較,若值indexLast的值比較小,
        // 則將indexLast從indexDeque刪除,這樣可以保證indexDeque中的索引在nums中的取值是降序排列,也能保證indexDeque中的
        // 首個(gè)值是滑動(dòng)窗口的最大值的索引
        while (indexDeque != null && indexDeque.size() > 0 && nums[index] > nums[indexDeque.getLast()]) {
            indexDeque.removeLast();
        }
        indexDeque.addLast(index);
    }
}

復(fù)雜度分析
時(shí)間復(fù)雜度: 在nums數(shù)組中遍歷獲得滑動(dòng)窗口O(N),雙端隊(duì)列刪除、獲取隊(duì)首元素的復(fù)雜度為O(1);添加元素的復(fù)雜度為O(1)~O(logk),
因此最終的時(shí)間復(fù)雜度為O(N)~O(log(k));
空間復(fù)雜度:返回值所需O(N-l+1),雙端隊(duì)列為O(k),最終為O(N);

動(dòng)態(tài)規(guī)劃

思路
構(gòu)造left,right數(shù)組;

  • 將nums按k的大小分成多段,最后一段可能不到k個(gè)元素;
  • left數(shù)組中,left中按分段與nums對(duì)應(yīng),left分段中從左到右對(duì)應(yīng)索引的值,是nums對(duì)應(yīng)分段中對(duì)應(yīng)索引及之前的索引的之中最大的一個(gè)值
  • right數(shù)組中,right中按分段與nums對(duì)應(yīng),right分段中從右到左對(duì)應(yīng)索引的值,是nums對(duì)應(yīng)分段中對(duì)應(yīng)索引及之前的索引的之中最大的一個(gè)值
  • 因此獲取一個(gè)滑動(dòng)窗口的最大值,是max(滑動(dòng)窗口最左邊位置的索引在right數(shù)組中的值,滑動(dòng)窗口最右邊位置的索引在left數(shù)組中的值

代碼

public class DynamicProgrammingSolution implements ISolution {
    @Override
    public int[] maxSlidingWindow(int[] nums, int k) {
        int len = nums.length;
        if (k > len || k < 1) {
            return new int[0];
        }
        int[] left=new int[len];
        int[] right=new int[len];
        //構(gòu)造left、right數(shù)組
       for(int i=0;i<len;i++){
            //left構(gòu)造
            if(i%k==0){
                left[i]=nums[i];
            }else{
                left[i]=Math.max(left[i-1],nums[i]);
            }

            //right構(gòu)造,從nums數(shù)組右邊開(kāi)始遍歷
            int index=len-i-1;
            if(index==len-1||(index+1)%k==0){
                right[index]=nums[index];
            }else{
                right[index]=Math.max(right[index+1],nums[index]);
            }
        }

        //獲取滑動(dòng)窗口最大值數(shù)組
        int[] result=new int[len-k+1];
        for(int i=0;i<len-k+1;i++){
            result[i]=Math.max(right[i],left[i+k-1]);
        }
        return result;
    }
}

復(fù)雜度分析
時(shí)間復(fù)雜度: 構(gòu)造left,rigt數(shù)組O(N),在nums數(shù)組中遍歷獲得滑動(dòng)窗口O(N).
因此最終的時(shí)間復(fù)雜度為O(2N);
空間復(fù)雜度:返回值所需O(N-l+1),left,right數(shù)組分別為O(N),最終為O(3
N);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 關(guān)閉窗戶讓噪聲遠(yuǎn)離 這空間如此壓抑 轟隆聲仍滲透過(guò)縫隙 像不散的陰魂不肯分離 我漸漸沉浸嘈雜 就當(dāng)做是種自我麻痹 ...
    喝奶茶不胖閱讀 239評(píng)論 4 3
  • 臺(tái)北動(dòng)物園是亞洲第一大動(dòng)物園,也是電影少年派奇幻漂流取景地。 先說(shuō)去動(dòng)物園的路上,臺(tái)北五號(hào)線捷運(yùn)都做過(guò),最喜歡的是...
    妙蓉小童閱讀 362評(píng)論 0 2
  • 又感冒,大中午就被接回家,睡醒后又活蹦亂跳! 包餃子 皮兒搟得很棒,刀拿起來(lái)像模像樣的。 娃說(shuō)她切肉,切完煮了吃。...
    Joyce_kexin閱讀 143評(píng)論 0 1
  • 出生前 我們都會(huì)給自己編好劇本 一些喜悅 一些彩蛋 一個(gè)謎團(tuán)。你準(zhǔn)備好了嗎?記得一直在尋覓愛(ài) 也一直在想自己為什么...
    贊跬步閱讀 383評(píng)論 0 0

友情鏈接更多精彩內(nèi)容