单调队列 Monotonic Queue应用:leetcode(未完待续)
239. Sliding Window Maximum
581: https://leetcode.com/problems/shortest-unsorted-continuous-subarray/
862: https://leetcode.com/problems/shortest-subarray-with-sum-at-least-k/
496: https://leetcode.com/problems/next-greater-element-i/
84: https://leetcode.com/problems/largest-rectangle-in-histogram/
239:最主要是push
class Solution { public: //单调队列:单调递增或单调递减队列 class MQ { deque<pair<int,int>> mq; //first为数组下标。second为push该数时前面remove掉的数的个数(保持MQ中第一个元素为最大值,可能会pop掉之前的好几个元素) //那么,每次在front_pop时,second为0,才会pop该元素,否则,second-1(其实可以理解second为队列中总的元素数) public: void push(int val){ //如果为空队列,直接push if(mq.empty()){ mq.push_back(make_pair(val,0)); return ; } //双端队列不为空,remove掉前面小于该val的数,保证最大的为队头. //而且保证队列中为递减,只保留最大、次大。。。 //3 1 在队列,现在push 2: 如何做? int cnt = 0; while(!mq.empty() && mq.back().first < val){ cnt = cnt + mq.back().second+1; mq.pop_back(); } mq.push_back(make_pair(val,cnt)); } //pop的时候,pop队头,但是要保证队头元素的second属性为0. void pop(){ if(!mq.empty() && mq.front().second){ mq.front().second--; return ; } if(!mq.empty()) mq.pop_front(); } int getMax(){ return mq.front().first; } }; vector<int> maxSlidingWindow(vector<int>& nums, int k) { //每一次push,getMax,pop int n = nums.size(); vector<int> res; deque<pair<int,int>> dq; MQ mq; for(int i=0;i<n;i++){ mq.push(nums[i]); if(i >= k-1){ res.push_back(mq.getMax()); mq.pop(); } } return res; } };
So we maintain a monotonic array with index increasing and value decreasing, because smaller elements like A[l]
on the left are useless.
For example, with sliding window of fixed length 3,
A = [3, 1, 4, 3, 8] => monotonic queue is like [3], [3, 1], [4], [4, 3], [8]
when element 4
enters, we remove [3, 1]
because they are on the left and smaller than 4
, no chance being chosen as the max element.
The head of the increasing queue is the running max!
class Solution { public: //mq存nums数组下标 vector<int> maxSlidingWindow(vector<int>& nums, int k) { deque<int> mq; vector<int> res; int n = nums.size(); for(int i=0;i<n;i++){ if(!mq.empty() && i-mq.front() >=k) mq.pop_front(); //push进去 while(!mq.empty() && nums[mq.back()] < nums[i]){ mq.pop_back(); } mq.push_back(i); if(i >= k-1) res.push_back(nums[mq.front()]); } return res; } };
581: 最短无序连续子数组
class Solution { public: //基本方法利用排序,前后进行数组对比 int findUnsortedSubarray(vector<int>& nums) { int n = nums.size(); int res = n; int s=n,e=-1; //1 3 5 6 7 2 8 9 //超时,优化。s:位于数组中最左边的其后有元素小于nums[s]的位置,e:位于数组最右边其前有元素大于nums[e]的位置 // for(int i=0;i<n;i++){ // for(int j=i+1;j<n;j++){ // if(nums[i] > nums[j]) { // s = i; // e = j; // } // if(s != e){ // start.push_back(s); // end.push_back(e); // } // } // } //return start.size()==0 ? 0:*max_element(end.begin(),end.end())-start.front()+1; //利用栈: 找出最左和左右位置。 //当nums[j] >= 栈顶 一直push(升序),当num[j] < 栈顶开始pop直到num[j]>栈顶,此时栈顶位置为s(所有都遍历完) //同理:反序一遍找e stack<int> sta; for(int i=0;i<n;i++){ while(!sta.empty() && nums[i] < nums[sta.top()]) { s = min(s,sta.top()); sta.pop(); } sta.push(i); } while(!sta.empty()) sta.pop(); for(int i=n-1;i>=0;i--){ while(!sta.empty() && nums[i] > nums[sta.top()]) { e = max(e,sta.top()); sta.pop(); } sta.push(i); } return s-e >n ? 0 : e-s+1; } };
上述算法O(N) spce,time
优化:找出无序数组部分的最小值和最大值,再找到小于最小值的位置和大于最大值的位置(直接即为无序部分)
我们再次遍历 numsnums 数组并通过与其他元素进行比较,来找到 minmin 和 maxmax 在原数组中的正确位置。我们只需要从头开始找到第一个大于 minmin 的元素,从尾开始找到第一个小于 maxmax 的元素,它们之间就是最短无序子数组。
class Solution { public: //找数组中无序部分最小与最大 //再找到小于min的位置,大于max的位置 int findUnsortedSubarray(vector<int>& nums) { int beg = -1,end = -2; int n = nums.size(); int mn = INT_MAX,mx = INT_MIN; int flag = false; for(int i=1;i<n;i++){ if(nums[i] < nums[i-1]) flag = true; //后面都是无序部分 if(flag){ if(nums[i] < mn) mn = nums[i]; } } flag = false; for(int i=n-2;i>=0;i--){ if(nums[i] > nums[i+1]) flag = true; if(flag){ if(nums[i] > mx) mx = nums[i]; } } //找出小于min的位置和大于max的位置,这个位置直接都无序 int l = n,r = -1; for(int i=0;i<n;i++){ if(nums[i] > mn) l = min(l,i); if(nums[i] < mx) r = max(r,i); } return l-r > n ? 0:r-l+1; } };
862: Shortest Subarray with Sum at Least K
class Solution { public: //deque+前缀和. //前缀和 {pre[i] <= pre[j]-K} int shortestSubarray(vector<int>& A, int K) { int n = A.size(); int res = n+1; //最主要的是首先得想到前缀和:这个问题典型的前缀和 vector<int> pre; int sum = 0; for(int i=0;i<n;i++){ sum += A[i]; pre.push_back(sum); } //问题转化为:pre[j]-pre[i] >= K.即对于当前的pre[j]来说,找一个pre[i]最小,且距离j最近 //左侧 最近最小的元素(单调递减序列) deque<int> dq; for(int i=0;i<n;i++){ if(pre[i] >= K) res = min(res,i+1); //首先进行结果判断 while(!dq.empty() && pre[i]-pre[dq.front()]>=K){ res = min(res,i-dq.front()); dq.pop_front(); } //将当前pre[i] 入队尾,尾部比当前pre[i]大的元素无机会再留在队列内le while(!dq.empty() && pre[i] <= pre[dq.back()]){ dq.pop_back(); } dq.push_back(i); } return res == n+1 ? -1:res; } };
496: