日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

嗯,查询滑动窗口最大值的这4种方法不错....

發布時間:2025/3/11 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 嗯,查询滑动窗口最大值的这4种方法不错.... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


作者 | 王磊

來源 | Java中文社群(ID:javacn666)

轉載請聯系授權(微信ID:GG_Stone)

本文已收錄至 Github《小白學算法》系列:https://github.com/vipstone/algorithm

這是一道比較基礎的算法題,涉及到的數據結構也是我們之前講過的,我這里先買一個關子。這道面試題最近半年在亞馬遜的面試中出現過 28 次,在字節跳動中出現過 7 次,數據來源于 LeetCode。

我們先來看題目的描述。

題目描述

給定一個數組 nums 和滑動窗口的大小 k,請找出所有滑動窗口里的最大值。

示例:

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

提示:你可以假設 k 總是有效的,在輸入數組不為空的情況下,1 ≤ k ≤?輸入數組的大小。

LeetCode:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/

題目解析

上面的題目看不懂?沒關系,接下來來看這幅圖可以清楚的描述這道題:從上述圖片可以看出,題目的意思為:給定一個數組,每次查詢 3 個元素中的最大值,數量 3 為滑動窗口的大小,之后依次向后移動查詢相鄰 3 個元素的最大值。圖片中的原始數組為 [1,3,-1,-3,5,3,6,7],最終滑動窗口的最大值為 [3,3,5,5,6,7]。

看到這個題之后,我們的第一直覺就是暴力解法,用兩層循環依次查詢滑動窗口的最大值,實現代碼如下。

實現方法 1:暴力解法

暴力解法的實現思路和實現代碼很直觀,如下所示:

class?Solution?{public?int[]?maxSlidingWindow(int[]?nums,?int?k)?{//?非空判斷if?(nums?==?null?||?k?<=?0)?return?new?int[0];//?最終結果數組int[]?res?=?new?int[nums.length?-?k?+?1];for?(int?i?=?0;?i?<?res.length;?i++)?{//?初始化最大值int?max?=?nums[i];?//?循環?k-1?次找最大值for?(int?j?=?i?+?1;?j?<?(i?+?k);?j++)?{max?=?(nums[j]?>?max)???nums[j]?:?max;}res[i]?=?max;}return?res;} }

把以上代碼提交至 LeetCode,執行結果如下:從上述結果可以看出,雖然代碼通過了測試,但執行效率卻很低,這種代碼是不能應用于生產環境中的,因此我們需要繼續找尋新的解決方案。

實現方法 2:改良版

接下來我們稍微優化一下上面的方法,其實我們并不需要每次都經過兩層循環,我們只需要一層循環拿到滑動窗口的最大值(之前循環元素的最大值),然后在移除元素時,判斷當前要移除的元素是否為滑動窗口的最大值,如果是,則進行第二層循環來找到新的滑動窗口的最大值,否則只需要將最大值和新增的元素比較大小即可,實現代碼如下:

class?Solution?{public?int[]?maxSlidingWindow(int[]?nums,?int?k)?{//?非空判斷if?(nums?==?null?||?k?<=?0)?return?new?int[0];//?最終結果數組int[]?res?=?new?int[nums.length?-?k?+?1];//?上一輪循環移除的值int?r?=?-Integer.MAX_VALUE;?//?滑動窗口最大值(初始化)int?max?=?r;?for?(int?i?=?0;?i?<?res.length;?i++)?{//?1.判斷移除的值,是否為滑動窗口的最大值if?(r?==?max)?{//?2.移除的是滑動窗口的最大值,循環找到新的滑動窗口的最大值max?=?nums[i];?//?初始化最大值//?循環找最大值for?(int?j?=?i?+?1;?j?<?(i?+?k);?j++)?{max?=?Math.max(max,?nums[j]);}}?else?{//?3.只需要用滑動窗口的最大值和新增值比較即可max?=?Math.max(max,?nums[i?+?k?-?1]);}//?最終的返回數組記錄res[i]?=?max;//?記錄下輪要移除的元素r?=?nums[i];}return?res;} }

把以上代碼提交至 LeetCode,執行結果如下:從上述結果可以看出,改造之后的性能基本已經符合我的要求了,那文章開頭說過這道題還可以使用我們之前學過的數據結構?那它說的是什么數據結構呢?

其實我們可以使用「隊列」來實現這道題目,它的實現思路也非常簡單,甚至比暴力解法更加方便,接下來我們繼續往下看。

實現方法 3:優先隊列

這個題的另一種經典的解法,就是使用最大堆的方式來解決,最大堆的結構如下所示:最大堆的特性是堆頂是整個堆中最大的元素

我們可以將滑動窗口的值放入最大堆中,這樣利用此數據結構的特點(它會將最大值放到堆頂),因此我們就可以直接獲取到滑動窗口的最大值了,實現代碼如下:

class?Solution?{public?int[]?maxSlidingWindow(int[]?nums,?int?k)?{//?非空判斷if?(nums?==?null?||?k?<=?0)?return?new?int[]{};//?最終結果數組int[]?res?=?new?int[nums.length?-?k?+?1];//?優先隊列PriorityQueue<Integer>?queue?=?new?PriorityQueue(res.length,?new?Comparator<Integer>()?{@Overridepublic?int?compare(Integer?i1,?Integer?i2)?{//?倒序排列(從大到小,默認是從小到大)return?i2?-?i1;}});//?第一輪元素添加for?(int?i?=?0;?i?<?k;?i++)?{queue.offer(nums[i]);}res[0]?=?queue.peek();int?last?=?nums[0];?//?每輪要移除的元素for?(int?i?=?k;?i?<?nums.length;?i++)?{//?移除滑動窗口之外的元素queue.remove(last);//?添加新元素queue.offer(nums[i]);//?存入最大值res[i?-?k?+?1]?=?queue.peek();//?記錄每輪要移除的元素(滑動窗口最左邊的元素)last?=?nums[i?-?k?+?1];}return?res;} }

代碼解讀

從上述代碼可以看出:最大堆在 Java 中對應的數據結構就是優先級隊列 PriorityQueue,但優先級隊列默認的排序規則是從小到大進行排序的,因此我們需要創建一個 Comparator?來改變一下排序的規則(從大到小進行排序),之后將滑動窗口的所有元素放入到優先級隊列中,這樣我們就可以直接使用 queue.peek()?拿到滑動窗口的最大值了,然后再循環將滑動窗口的邊緣值移除掉,從而解決了本道題目。

把以上代碼提交至 LeetCode,執行結果如下:

PS:從上面的執行結果可以看出,使用優先隊列的執行效率很低,這是因為每次插入和刪除都需要重新維護最大堆的元素順序,因此整個執行的效率就會很低。

實現方法 4:雙端隊列

除了優先隊列之外,我們還可以使用雙端隊列來查詢滑動窗口的最大值,它的實現思路和最大堆的實現思路很像,但并不需要每次在添加和刪除時進行元素位置的維護,因此它的執行效率會很高。

雙端隊列實現思路的核心是將滑動窗口的最大值始終放在隊首位置(也就是隊列的最左邊),將小于最大值并在最大值左邊(隊首方向)的所有元素刪除。這個也很好理解,因為這些相對較小的值既沒有最大值大,又在最大值的前面,也就是它們的生命周期比最大值還短,因此我們可以直接將這些相對較小的元素進行刪除,如下圖所示:

像以上這種情況下,我們就可以將元素 1 和元素 2 刪掉。

雙端隊列實現查詢滑動窗口最大值的流程分為以下 4 步:

  • 移除最左邊小于最大值的元素(保證滑動窗口的最大值在隊首位置);

  • 從隊尾向前依次移除小于當前要加入到隊列元素的值(淘汰小值且生命周期短的元素);

  • 將新元素加入到隊列末尾;

  • 將最大值加入到最終結果的數組中。

  • 實現代碼如下:

    class?Solution?{public?int[]?maxSlidingWindow(int[]?nums,?int?k)?{//?非空判斷if?(nums?==?null?||?k?<=?0)?return?new?int[0];//?最終結果數組int[]?res?=?new?int[nums.length?-?k?+?1];//?存儲的數據為元素的下標ArrayDeque<Integer>?deque?=?new?ArrayDeque();for?(int?i?=?0;?i?<?nums.length;?i++)?{//?1.移除左邊超過滑動窗口的下標if?(i?>=?k?&&?(i?-?k)?>=?deque.peek())?deque.removeFirst();//?2.從最后面開始移除小于?nums[i]?的元素while?(!deque.isEmpty()?&&?nums[deque.peekLast()]?<?nums[i])deque.removeLast();//?3.下標加入隊列deque.offer(i);//?4.將最大值加入數組int?rindex?=?i?-?k?+?1;if?(rindex?>=?0)?{res[rindex]?=?nums[deque.peek()];}}return?res;} }

    把以上代碼提交至 LeetCode,執行結果如下:

    從上述結果可以看出,雙端隊列相比于優先級隊列來說,因為無需重新計算并維護元素的位置,所以執行效率還是挺高的。

    總結

    本文我們通過 4 種方式實現了查找滑動窗口最大值的功能,其中暴力解法通過兩層循環來實現此功能,代碼最簡單但執行效率不高,而通過最大堆也就是優先隊列的方式來實現(本題)雖然比較省事,但執行效率不高。因此我們可以選擇使用雙端隊列或改良版的代碼來實現查詢滑動窗口的最大值。

    往期推薦

    真不錯,圖解Java中的5大隊列!


    23張圖!萬字詳解「鏈表」,從小白到大佬!


    隊列實現棧的3種方法,全都擊敗了100%的用戶!


    關注我,每天陪你進步一點點!

    總結

    以上是生活随笔為你收集整理的嗯,查询滑动窗口最大值的这4种方法不错....的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。