题目
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
分析
求每个窗口内部的最大值问题,就等同于求连续特定区间内部的最大值最小值问题,这就是单调队列的应用之一。
单调队列用双端队列实现。常用的queue在没有指定容器的条件下,用的底层就是双端队列。注意:单调队列不是并不是优先级队列,保存了所有的元素。它只保留可能成为最大(小)元素。
设计单调(递减)队列的规则:
1. 如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
2.如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止
上面是代码随想录的Carl大佬总结。
代码
1 class Solution { 2 private: 3 //单调队列,用dequeue实现从大到小 4 class MyQueue{ 5 public: 6 deque<int>que; 7 8 //每次需要弹出元素,要与单调队列开头元素比较,如果和开头元素相同,则弹出 9 //否则,不做任何操作 10 void pop(int x){ 11 if(!que.empty() && x == que.front()){ 12 que.pop_front(); //从双端队列头部弹出,该元素是最大元素 13 } 14 } 15 16 //每次需要进入元素,要与单调队列尾部部元素进行比较。 17 //如果要进入元素比单调队列元素的值大,就将单调队列中小于要进入的元素从队列尾部弹出 18 //直到push的值小于等于单调队列元素为止。这样就保持了单调递减的性质 19 void push(int x){ 20 while(!que.empty() && x > que.back()){ 21 que.pop_back(); 22 } 23 que.push_back(x);//从队列尾部加入元素x 24 } 25 int front(){ 26 return que.front(); 27 } 28 }; 29 public: 30 vector<int> maxSlidingWindow(vector<int>& nums, int k) { 31 MyQueue que; 32 vector<int>res; 33 //首先将前k个加入单调队列 34 for(int i = 0;i < k;i++){ 35 que.push(nums[i]); 36 } 37 res.push_back(que.front()); 38 //开始进行窗口滑动,窗口每向前移动一次,窗口要移除元素,加入元素 39 //并且返回单调队列的头部元素(保存了最大值)加入res 40 for(int i = k;i < nums.size();i++){ 41 que.pop(nums[i - k]); //窗口移除元素 42 que.push(nums[i]);//窗口加入元素 43 res.push_back(que.front()); 44 } 45 return res; 46 47 } 48 };
时间复杂度O(n),空间复杂度O(k)