单调队列及其经典问题

单调队列适合维护区间最值问题(滑动窗口最值问题)
单调队列:
入队操作:队尾入队,会把之前破坏单调性的元素都从队尾移除(维护单调性)。
出队操作:如果队首元素超出区间范围,就将元素从队首出队。
元素性质:队首元素,永远是当前维护区间的(最大或者最小)值。序列中的每一个元素,在依次入队的过程中
要点:在记录数据时最好记录元信息
HZOJ-271

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <vector>
using namespace std;
 
 int main(){
     int n,k;
     vector<int>arr;
     cin>>n>>k;
     for(int i=0,a;i<n;i++){
         cin>>a;
         arr.push_back(a);
     }
    //维护最小值需要一个单调递增队列(这里我们使用双端队列(允许头、尾插入和删除))
    deque<int> q;//队列中记录的不是相应元素的值而是相应元素的下标
    for(int i=0;i<n;i++){//单调递增序列
        while(q.size() && arr[q.back()]>arr[i])q.pop_back();//处理单调队列尾部(入队)
        q.push_back(i);
        if(i-q.front() == k)q.pop_front();//处理单调队列头部(如果超出滑动窗口长度)(出队)
        if(i+1<k) continue;
        if(i+1>k)cout<<" ";
        cout<<arr[q.front()];
    }
    q.clear();
    for(int i=0;i<n;i++){//单调递减序列
        while(q.size() && arr[q.back()] < arr[i])q.pop_back();//处理单调队列尾部(入队)
        q.push_back(i);
        if(i-q.front() == k)q.pop_front();//处理单调队列头部(如果超出滑动窗口长度)(出队)
        if(i+1<k) continue;
        if(i+1>k)cout<<" ";
        cout<<arr[q.front()];
    }
     return 0;
 }

HZOJ-372(双生序列)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <vector>
using namespace std;
//双生序列
 int main(){
     int n;//代表a、b序列的长度
     cin>>n;
     vector<int>a(n),b(n);
     for(int i=0;i<n;i++) cin>>a[i];
     for(int i=0;i<n;i++) cin>>b[i];
    deque<int> q1,q2;//单调递增队列
    int p;
    for(p=0;p<n;p++){
        while(q1.size()&&a[p]<q1.back())q1.pop_back();
        while(q2.size()&&a[p]<q2.back())q2.pop_back();
        q1.push_back(a[p]);
        q2.push_back(b[p]);
        if(q1.size()!=q2.size()) break;
    }
    cout<<p<<endl;
     return 0;
 }

leetcode
239.滑动窗口最大值

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int> q;
        vector<int> ret;
        for(int i=0;i<nums.size();i++){
            while(q.size() && nums[q.back()]<nums[i]) q.pop_back();
            q.push_back(i);
            if(i-q.front() == k)q.pop_front();
            if(i+1<k)continue;
            ret.push_back(nums[q.front()]);
        }
        return ret;
    }
};

剑指 Offer 59 - II. 队列的最大值

class MaxQueue {
public:
    deque<int> q,mq;//原始队列和单调队列
    MaxQueue() {}
    
    int max_value() {
        if(mq.size() == 0) return -1;
        return mq.front();
    }
    
    void push_back(int value) {
        q.push_back(value);
        while(mq.size() && value>mq.back()) mq.pop_back();//这里只能>是为了防止原序列中相同的值在单调队列中存在二义性
        mq.push_back(value);
        return ;
    }
    
    int pop_front() {
        if(q.size()==0) return -1;
        if(q.front() == mq.front()) mq.pop_front();
        int ret = q.front();
        q.pop_front();
        return ret;
    }
};
  1. 和至少为 K 的最短子数组
class Solution {
public:
    int shortestSubarray(vector<int>& nums, int k) {
        deque<int> q;
        vector<int> sum(nums.size()+1);//前缀和数组 注意大小要加1
        sum[0] = 0;
        for(int i=0;i<nums.size();i++)sum[i+1] =sum[i]+nums[i]; 
        //然后依次扫描前缀和数组中的每位
        //首先将前缀和数组第一位的下标压入到队列中
        q.push_back(0);//存下标!!
        //pos记录上一次找到答案的位置
        int pos = -1,ans = -1;
        for(int i=1;i<sum.size();i++){
            while(q.size() && sum[i]-sum[q.front()]>=k){
                pos = q.front();
                q.pop_front();
            }
            if(pos!=-1 && (i-pos<ans || ans == -1)) ans = i-pos;
            while(q.size() && sum[i]<sum[q.back()]) q.pop_back();
            q.push_back(i);
        }
        return ans;
    }
};
  1. 绝对差不超过限制的最长连续子数组
class Solution {
public:
    bool check(vector<int> &nums,int k,int limit){//长度为k
        deque<int> qmin,qmax;//分别维护相关窗口最小值和最大值
        for(int i=0;i<nums.size();i++){
            while(qmin.size() && nums[i]<nums[qmin.back()])qmin.pop_back();
            while(qmax.size() && nums[i]>nums[qmax.back()])qmax.pop_back();
            qmin.push_back(i);
            qmax.push_back(i);
            if(i+1 <k)  continue;
            if(i-qmin.front() == k) qmin.pop_front();
            if(i-qmax.front() == k) qmax.pop_front();
            if(nums[qmax.front()]-nums[qmin.front()]<=limit)return true;
        }
        return false;
    }
    int bs(vector<int> &nums,int l,int r,int limit){
        if(l==r) return l;//说明找到了这个位置
        int mid=(l+r+1)>>1;//10模型算中间值时候需要加1
        if(check(nums,mid,limit)) l = mid;
        else r = mid -1;
        return bs(nums,l,r,limit);
    }
    int longestSubarray(vector<int>& nums, int limit) {
        return bs(nums,1,nums.size(),limit);//设计二分10模型
    }
};
  1. 找树左下角的值
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int max_k,val;//最大深度和值 
    void dfs(TreeNode* root, int k){//第二个参数代表当前所在层号
        if(root == nullptr) return ;
        if(k>max_k){
            max_k = k,val = root->val;
        }
        dfs(root->left,k+1);
        dfs(root->right,k+1);
        return ;
        

    }
    int findBottomLeftValue(TreeNode* root) {
        max_k =-1,val =0;
        dfs(root,0);
        return val;
    }
};
  1. 分发糖果
class Solution {
public:
    int candy(vector<int>& ratings) {
        vector<int>l(ratings.size()),r(ratings.size());
        //求从左到右的每个孩子的糖果值
        for(int i=0,j=1;i<l.size();i++){//j表示当前孩子得到的糖果数量
            if(i && ratings[i]>ratings[i-1]) j+=1;
            else j=1;
            l[i] = j;
        }
        //从右向左每个孩子的糖果值
        for(int i=r.size()-1,j=1;i>=0;i--){
            if(i<r.size()-1 && ratings[i]>ratings[i+1]) j+=1;
            else j=1;
            r[i] = j;
        }
        int ans = 0;
        for(int i=0;i<l.size();i++) ans += max(l[i],r[i]);
    return ans;
    }

};
  1. 水壶问题
class Solution {
public:
    typedef pair<int,int> PII;
    PII getNext(int k,int x,int X,int y,int Y){//通过当前状态获取下一个状态的函数
    //k表示要转移到第几种状态,x表示当前第一个壶中水的数量,X表示水的容量
        switch(k){
            case 0: return PII(0,y); //代表将第一个壶给清空
            case 1: return PII(x,0); //代表将第二个壶给清空
            case 2: {//代表将第一个壶的水倒入到第二个壶中一部分:需要考虑到第二个壶中剩余水的容量是多少
                int delta = min(x,Y-y);
                return PII(x-delta,y+delta);
            }
            case 3:{//代表将第二个壶的水倒入第一个壶中一部分
                int delta = min(X-x,y);
                return PII(x+delta,y-delta);
            }
            case 4:return PII(X,y);//将第一个壶灌满
            case 5:return PII(x,Y);//将第二个壶灌满
        }
        return PII(0,0);
    }
    struct HASH{
        long long operator()(const PII &a) const {
            return ((long long)(a.first) << 31) + a.second;
        } 
    };
    bool canMeasureWater(int jug1Capacity, int jug2Capacity, int targetCapacity) {
        unordered_set<PII,HASH> vis;
        queue<PII> q;
        vis.insert(PII(0,0));
        q.push(PII(0,0));
        while(!q.empty()){
            PII cur = q.front();
            if(cur.first+cur.second == targetCapacity) return true;
            q.pop();
            for(int i=0;i<6;i++){
                PII temp = getNext(i,cur.first,jug1Capacity,cur.second,jug2Capacity);
                if(vis.find(temp)!=vis.end())continue;//如果状态出现过则跳过
                vis.insert(temp);
                q.push(temp);
            }
        }
        return false;
    }
};
  1. 袋子里最少数目的球
class Solution {
public:
    int f(vector<int>&nums,int x){//传入球的数量,返回最小操作次数,x表示每一堆球的数量
        int cnt = 0;
        for(int i=0;i<nums.size();i++){
            cnt += nums[i]/x+!!(nums[i]%x)-1;
        }
        return cnt;
    }
    //二分01模型
    int bs(vector<int>&nums,int l,int r,int n){//n为最大操作次数
        if(l == r) return l ;
        int mid = (l+r)>>1;
        if(f(nums,mid)<=n)r=mid;
        else l = mid+1;
        return bs(nums,l,r,n);
    }
    int minimumSize(vector<int>& nums, int maxOperations) {
        int l =1,r;
        for(auto x:nums) r = max(r,x);
        return bs(nums,l,r,maxOperations); 
    }
};
  1. 跳跃游戏 II
class Solution {
public:
    int jump(vector<int>& nums) {
        if(nums.size()<=1) return 0;
        int pre=1,pos =nums[0],cnt =1;//pre表示第一次跳跃范围最近位置,pos表示第一次跳跃最远位置
        while(pos+1<nums.size()){
            int j = pre;//向后找到跳的最远的那个下标
            for(int i=pre+1;i<=pos;i++){
                if(nums[i]+i>j+nums[j]) j = i;
            }
            pre = pos +1, pos = j+nums[j];
            cnt+=1;
        }
        return cnt;
    }
};
  1. 复原 IP 地址
class Solution {
public:
    void dfs(string &s,int k,int ind,vector<string>&ret){
        if(ind >= s.size()) return ;
        if(k==4){
            int num = 0;//判断当前数字是否是合法的
            if(s.size() - ind>1 && s[ind] == '0')return ;//数字不合法
            for(int i=ind;i<s.size();i++){
                num = num*10+s[i]-'0';
                if(num>255) return ;//数字不合法
            }
        //此时说明s所代表的的最后一段数字合法
        ret.push_back(s);
        return ;
        }
        //接下来对ind后面进行遍历
        for(int i=ind,num=0;i<s.size();i++){//num用来存储最后这段数字
            num = num*10+s[i]-'0';
            if(num > 255) return ;
            if(i - ind >=1 && s[ind] == '0') return;
            s.insert(i+1,".");
            dfs(s,k+1,i+2,ret);
            s.erase(i+1,1);
        }
        return;

    }
    vector<string> restoreIpAddresses(string s) {
        vector<string> ret;
        dfs(s,1,0,ret);//第二个参数表示当前正在插入第几个点,第三个参数上一个合法位置
        return ret; 
    }
};
  1. 全排列
class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> ret;
        do{
            ret.push_back(nums);
        }while(next_permutation(nums.begin(),nums.end()));

        return ret;
    }
};
  1. 字符串相乘
class Solution {
public:
    string multiply(string num1, string num2) {
        vector<int>a(num1.size()),b(num2.size()),c(a.size()+b.size()-1);
        for(int i=0;i<num1.size();i++)a[a.size()-i-1] = num1[i]-'0';
        for(int i=0;i<num2.size();i++)b[b.size()-i-1] = num2[i]-'0';
        for(int i=0;i<a.size();i++){
            for(int j =0;j<b.size();j++){
                c[i+j]+=a[i]*b[j];
            }
        }
        //处理c数组的进位
        for(int i =0;i<c.size();i++){
            if(c[i]<10) continue;//不需要进位
            if(i+1 == c.size())//判断需要进位的位置是否为最高位
                c.push_back(0);//如果是最高位就加一位
            c[i+1] += c[i] / 10;
            c[i] %= 10;
        }
        //删掉前导0
        while(c.size()>1 && c[c.size() -1] == 0) c.pop_back();
        string ret ="";
        for(int i = c.size()-1;i>=0;i--) ret+=c[i]+'0';
        return ret;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值