最大值_Leetcode2 | 滑动窗口最大值(Q239)
:)
Sliding Window Maximum
Q 239
今天也是好心情
Problem Description
··· Difficulty···
You are given an array of integers nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.
Return the max sliding window.
Example 1:
Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
Output: [3,3,5,5,6,7]
Explanation:?
Window position? ? ? ? ? ? ? ? Max
---------------? ? ? ? ? ? ? ?-----
[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
暴力
遍歷
1
?Too Young
很樸素的循環遍歷+調用庫函數max找最值
也不出意外的TLE了:(
// Javascript版var maxSlidingWindow = function(nums, k) { let max = []; for(let i = 0; i < nums.length-k+1; i++){ let tmp = nums.slice(i,i+k); max.push(Math.max(...tmp)); } return max;};代碼實現起來真的很快很簡單,但TLE也很快的顯示了出來T^T
當我點進超時數據時,看傻了...(是我大意了)
Too Young Too Simple
官方
題解
2
? ?優先隊列
“優先隊列(Heap)
可實時維護一系列元素中的最大/小值”
????對于本題而言,初始時,將 nums 的前k個元素放入優先隊列中。每當我們向右移動窗口時,我們就可以把一個新的元素放入優先隊列中,此時堆頂的元素就是堆中所有元素的最大值。然而這個最大值可能并不在滑動窗口中,在這種情況下,這個值在數組 nums 中的位置出現在滑動窗口左邊界的左側。因此,當我們后續繼續向右移動窗口時,這個值就永遠不可能出現在滑動窗口中了,我們可以將其永久地從優先隊列中移除。
????我們不斷地移除堆頂的元素,直到其確實出現在滑動窗口中。此時,堆頂元素就是滑動窗口中的最大值。為了方便判斷堆頂元素與滑動窗口的位置關系,我們可以在優先隊列中存儲二元組(num,index),表示元素num在數組中的下標為index。
class Solution {public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { int n = nums.size(); priority_queueint, for (int i = 0; i < k; ++i) { q.emplace(nums[i], i); } vector<int> ans = {q.top().first}; for (int i = k; i < n; ++i) { q.emplace(nums[i], i); while (q.top().second <= i - k) { q.pop(); } ans.push_back(q.top().first); } return ans; }};時間復雜度?O(nlogn)
最壞情況下,數組nums 中的元素單調遞增,那么最終優先隊列中包含了所有元素,沒有元素被移除。由于將一個元素放入優先隊列的時間復雜度為O(logn),因此總時間復雜度為O(nlogn)。
空間復雜度?O(n)
官方
題解
3
? ?單調隊列
“優先隊列方法的優化版”
????由于我們需要求出的是滑動窗口的最大值,如果當前的滑動窗口中有兩個下標i和j,其中i在j的左側(i
????當滑動窗口向右移動時,只要i還在窗口中,那么j一定也還在窗口中,這是i在j的左側所保證的。因此,由于 nums[j] 的存在,nums[i] 一定不會是滑動窗口中的最大值了,我們可以將nums[i] 永久地移除。
????因此我們可以使用一個隊列存儲所有還沒有被移除的下標。在隊列中,這些下標按照從小到大的順序被存儲,并且它們在數組nums 中對應的值是嚴格單調遞減的。因為如果隊列中有兩個相鄰的下標,它們對應的值相等或者遞增,那么令前者為i,后者為j,就對應了上面所說的情況,即nums[i] 會被移除,這就產生了矛盾。
????當滑動窗口向右移動時,我們需要把一個新的元素放入隊列中。為了保持隊列的性質,我們會不斷地將新的元素與隊尾的元素相比較,如果前者大于等于后者,那么隊尾的元素就可以被永久地移除,我們將其彈出隊列。我們需要不斷地進行此項操作,直到隊列為空或者新的元素小于隊尾的元素。
????由于隊列中下標對應的元素是嚴格單調遞減的,因此此時隊首下標對應的元素就是滑動窗口中的最大值。但與方法一中相同的是,此時的最大值可能在滑動窗口左邊界的左側,并且隨著窗口向右移動,它永遠不可能出現在滑動窗口中了。因此我們還需要不斷從隊首彈出元素,直到隊首元素在窗口中為止。
????為了可以同時彈出隊首和隊尾的元素,我們需要使用雙端隊列。滿足這種單調性的雙端隊列一般稱作「單調隊列」。
class Solution {public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { int n = nums.size(); deque<int> q; for (int i = 0; i < k; ++i) { while (!q.empty() && nums[i] >= nums[q.back()]) { q.pop_back(); } q.push_back(i); } vector<int> ans = {nums[q.front()]}; for (int i = k; i < n; ++i) { while (!q.empty() && nums[i] >= nums[q.back()]) { q.pop_back(); } q.push_back(i); while (q.front() <= i - k) { q.pop_front(); } ans.push_back(nums[q.front()]); } return ans; }};時間復雜度?O(nlogn)
最壞情況下,數組nums 中的元素單調遞增,那么最終優先隊列中包含了所有元素,沒有元素被移除。由于將一個元素放入優先隊列的時間復雜度為O(logn),因此總時間復雜度為O(nlogn)。
空間復雜度?O(n)
官方
題解
4
?分塊+預處理
“類似稀疏表(Sparse Table)的一種思路”
????可以將數組 nums 從左到右按照 k 個一組進行分組,最后一組中元素的數量可能會不足 k 個。如果我們希望求nums[i]到nums[i+k?1] 的最大值,就會有兩種情況:
如果i是k的倍數,那么nums[i]到nums[i+k?1]恰好是一個分組。我們只要預處理出每個分組中的最大值,即可得到答案;
如果i不是k的倍數,那么nums[i]到nums[i+k?1]會跨越兩個分組,占有第一個分組的后綴以及第二個分組的前綴。假設j是k的倍數,并且滿足i
需要注意在遞推suffixMax[i] 時需要考慮到邊界條件 suffixMax[n?1]=nums[n?1],而在遞推 prefixMax[i] 時的邊界條件prefixMax[0]=nums[0] 恰好包含在遞推式的第一種情況中,因此無需特殊考慮。
在預處理完成之后,對nums[i] 到nums[i+k?1] 的所有元素,如果i不是k的倍數,那么窗口中的最大值為 suffixMax[i]和prefixMax[i+k?1] 中的較大值;如果i是k的倍數,那么此時窗口恰好對應一整個分組,suffixMax[i]和prefixMax[i+k?1] 都等于分組中的最大值,因此無論窗口屬于哪一種情況
max{suffixMax[i],prefixMax[i+k?1]}
即為所求
class Solution {public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { int n = nums.size(); vector<int> prefixMax(n), suffixMax(n); for (int i = 0; i < n; ++i) { if (i % k == 0) { prefixMax[i] = nums[i]; } else { prefixMax[i] = max(prefixMax[i - 1], nums[i]); } } for (int i = n - 1; i >= 0; --i) { if (i == n - 1 || (i + 1) % k == 0) { suffixMax[i] = nums[i]; } else { suffixMax[i] = max(suffixMax[i + 1], nums[i]); } } vector<int> ans; for (int i = 0; i <= n - k; ++i) { ans.push_back(max(suffixMax[i], prefixMax[i + k - 1])); } return ans; }};時間復雜度?O(n)
空間復雜度?O(n)
—END—
題目和題解均源自LEETCODE?:)
總結
以上是生活随笔為你收集整理的最大值_Leetcode2 | 滑动窗口最大值(Q239)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C/C++ atoi函数 - C语言零基
- 下一篇: html 页面重复度高,html – C