E-Dreamer

脚踏实地,仰望星空

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

使用单调队列解决滑动窗口的最大值

#include <iostream>
#include <deque>
#include <vector>
using namespace std;
class MonotonicQueue { // 构建单调队列
private:
        deque<int> data;
public:
        void push(int n) {
               while (!data.empty() && data.back() < n) {
                       data.pop_back();
               }
               data.push_back(n);
        }
        int max() {
               return data.front();
        }
        void pop(int n) {
               if (!data.empty() && data.front() == n) {
                       data.pop_front();
               }
        }
};
class Solution {
public:
        // 使用单调队列
        vector<int> maxWindowSlide(vector<int>& nums, int k) {
               vector<int> res;
               MonotonicQueue window;
               for (int i = 0; i < nums.size(); i++) {
                       if (i < k - 1) {
                              window.push(nums[i]);
                       }
                       else {
                              window.push(nums[i]);
                              res.push_back(window.max());
                              window.pop(nums[i-k+1]);
                       }
               }
               return res;
        }
        /*
        遍历数组,将数存放在双向队列中,并用L,R来标记窗口的左边界和右边界。队列中保存的并不是真的数,
        而是该数值对应的数组下标位置,并且数组中的数要从大到小排序。如果当前遍历的数比队尾的值大,则需要弹出队尾值,
        直到队列重新满足从大到小的要求。刚开始遍历时,L和R都为0,有一个形成窗口的过程,此过程没有最大值,L不动,R向右移。
        当窗口大小形成时,L和R一起向右移,每次移动时,
        判断队首的值的数组下标是否在[L,R]中,如果不在则需要弹出队首的值,当前窗口的最大值即为队首的数。
        */
        // 使用双端队列
        vector<int> maxWindowSlide2(vector<int>& nums, int k) {
               vector<int> res;
               deque<int> dq;
               int i = 0;
               int j = 0;
               while(i< nums.size()) {
                       while (j < nums.size()) {
                              while (!dq.empty() && nums[dq.back()] < nums[j]) {
                                      dq.pop_back();
                              }
                              dq.push_back(j);
                              j++;
                       }
                       if (dq.front() <= i-k) {  // 判断当前队列中队首的值是否有效(过期)
                              dq.pop_front();
                       }
                       if (i >= k-1) {    // 当窗口长度为k时 保存当前窗口中最大值
                              res.push_back(nums[dq.front()]);
                       }
                       i++;
               }
               return res;
        }
};

 这里在记录一个别人的答案(双端队列解决),简短不失优雅。 

class Solution2 {
public:
        vector<int> maxInWindows(const vector<int>& num, unsigned int size)
        {
               vector<int> res;
               deque<int> s;
               for (unsigned int i = 0; i < num.size(); ++i) {
                       while (s.size() && num[s.back()] <= num[i])//从后面依次弹出队列中比当前num值小的元素,同时也能保证队列首元素为当前窗口最大值下标
                              s.pop_back();
                       while (s.size() && i - s.front() + 1 > size)//当当前窗口移出队首元素所在的位置,即队首元素坐标对应的num不在窗口中,需要弹出
                              s.pop_front();
                       s.push_back(i);//把每次滑动的num下标加入队列
                       if (size && i + 1 >= size)//当滑动窗口首地址i大于等于size时才开始写入窗口最大值
                              res.push_back(num[s.front()]);
               }
               return res;
        }
};
void main() {
        Solution S;
        vector<int> nums{ 9,7,10,5,8,3,6 };
        int k = 3;
        //vector<int> res = S.maxWindowSlide(nums, k);
        //for (auto s : res) {
        //      cout << s;
        //}
        //cout << endl;
        vector<int> res2 = S.maxWindowSlide2(nums, k);
        for (auto n : res2) {
               cout << n;
        }
}

  

posted on 2020-05-01 15:59  E-Dreamer  阅读(431)  评论(0编辑  收藏  举报