滑动窗口最大值
题目描述

题目分析
维护一个定长窗口的最大值,每当窗口滑动时都有一个新的元素进入和一个原有的元素离开。
 比较简单的方法就是用一个优先队列维护窗口最大值
 但是堆的计算成本时最坏时是 
     
      
       
       
         O 
        
       
         ( 
        
       
         n 
        
       
         log 
        
       
          
        
       
         n 
        
       
         ) 
        
       
      
        O(n\log n) 
       
      
    O(nlogn)
优化:
单调队列
 相比于堆,堆保留了全部元素,导致维护的成本较高,而单调队列是在保持顺序的情况下只保留符合单调性的元素,不需要重新重新调整结构,换句话说计算的复杂度最坏时为 
     
      
       
       
         O 
        
       
         ( 
        
       
         n 
        
       
         ) 
        
       
      
        O(n) 
       
      
    O(n)
 分块预处理
 将所有元素按滑动窗口的大小分块,我们可以得到 
     
      
       
       
         [ 
        
       
         n 
        
       
         / 
        
       
         k 
        
       
         ] 
        
       
         + 
        
       
         1 
        
       
      
        [n/k] + 1 
       
      
    [n/k]+1个块
 为了方便的得到块内最大值,我们可以参考前缀和的方式计算块内的前缀最大值,取 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i 为 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k的倍数时就可以取得第 
     
      
       
       
         i 
        
       
         / 
        
       
         k 
        
       
      
        i/k 
       
      
    i/k块的最大值
 问题在于,滑动窗口不一定就是我们分好的块
 所以考虑特殊情况,滑动窗口为 
     
      
       
       
         [ 
        
       
         i 
        
       
         , 
        
       
         j 
        
       
         ] 
        
       
      
        [i,j] 
       
      
    [i,j],设 
     
      
       
       
         m 
        
       
         ∈ 
        
       
         [ 
        
       
         i 
        
       
         , 
        
       
         j 
        
       
         ] 
        
       
         且 
        
       
         k 
        
       
         ∣ 
        
       
         m 
        
       
      
        m \in [i,j] 且 k\mid m 
       
      
    m∈[i,j]且k∣m(整除)
 则 
     
      
       
       
         p 
        
       
         r 
        
       
         e 
        
       
         m 
        
       
         a 
        
       
         x 
        
       
         [ 
        
       
         j 
        
       
         ] 
        
       
      
        premax[j] 
       
      
    premax[j]表示了 
     
      
       
       
         m 
        
       
         a 
        
       
         x 
        
       
         ( 
        
       
         n 
        
       
         u 
        
       
         m 
        
       
         s 
        
       
         [ 
        
       
         m 
        
       
         : 
        
       
         j 
        
       
         ] 
        
       
         ) 
        
       
      
        max(nums[m:j]) 
       
      
    max(nums[m:j])
 现在问题转为了怎么求 
     
      
       
       
         m 
        
       
         a 
        
       
         x 
        
       
         ( 
        
       
         n 
        
       
         u 
        
       
         m 
        
       
         s 
        
       
         [ 
        
       
         i 
        
       
         : 
        
       
         m 
        
       
         ) 
        
       
         ) 
        
       
      
        max(nums[i:m)) 
       
      
    max(nums[i:m))
 我们希望用 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i表示这一段的最大值。注意到 
     
      
       
       
         i 
        
       
         : 
        
       
         m 
        
       
      
        i:m 
       
      
    i:m是一种后缀形式,相应的我们可考虑块内的后缀最大值
 定义 
     
      
       
       
         s 
        
       
         u 
        
       
         f 
        
       
         m 
        
       
         a 
        
       
         x 
        
       
         [ 
        
       
         i 
        
       
         ] 
        
       
         表示了 
        
       
         m 
        
       
         a 
        
       
         x 
        
       
         ( 
        
       
         n 
        
       
         u 
        
       
         m 
        
       
         s 
        
       
         [ 
        
       
         i 
        
       
         : 
        
       
         m 
        
       
         ) 
        
       
         ) 
        
       
      
        sufmax[i]表示了max(nums[i:m)) 
       
      
    sufmax[i]表示了max(nums[i:m))
解决问题
1.优先队列
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int n = nums.size();
        priority_queue<pair<int, int>> q;
        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;
    }
};
2.单调队列
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;
    }
};
3.分块预处理
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;
    }
};
总结
优先队列,单调队列,动态维护最值
 分块预处理,空间换时间,从整体出发,问题分治,对称性思想

















