376摆动序列;377组合总和 Ⅳ;413等差数列划分;11盛最多水的容器;1288删除被覆盖区间;239滑动窗口最大值

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。

例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。

示例 1:

输入: [1,7,4,9,2,5]
输出: 6 
解释: 整个序列均为摆动序列。


示例 2:

输入: [1,17,5,10,13,15,10,5,16,8]
输出: 7
解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]。

示例 3:

输入: [1,2,3,4,5,6,7,8,9]
输出: 2

进阶:
你能否用 O(n) 时间复杂度完成此题?

class Solution {//动态规划 二维 N^2
public:
    int wiggleMaxLength(vector<int>& nums) {
        int nSize=nums.size();
        if(nSize<2)return nSize;
        vector<vector<int>> dp(nSize,vector<int>(2,1));//dp[i][0]表示nums[i]为下降的时候的长度,1为上升
        int resMax=1;
        for(int i=1;i<nSize;++i){
            for(int j=0;j<i;++j){
                if(nums[i]<nums[j])
                    dp[i][0]=max(dp[j][1]+1,dp[i][0]);
                else if(nums[i]>nums[j])
                    dp[i][1]=max(dp[j][0]+1,dp[i][1]);
            }
            resMax=max(resMax,dp[i][0]);
            resMax=max(resMax,dp[i][1]);
        }
        return resMax;
    }
};
class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        int nSize=nums.size();
        if(nSize<2)return nSize;
        int index=1,last=0,count=2;
        while(index<nSize&&nums[index]==nums[0])//找第一个和nums[0]不同的值
            ++index;
        if(index==nSize)return 1;//如果都相同
        bool flag=nums[0]<nums[index];//1:找大数 0:找小数
        for(;index<nSize;++index)
            if(flag)//找大数
                if(nums[last]<nums[index])//找到大数则换值
                    last=index;                
                else if(nums[last]>nums[index]){//找到小数则置flag,count加1,换值
                    flag=!flag;
                    ++count;
                    last=index;
                }
            else
                if(nums[last]>nums[index])
                    last=index++;                
                else if(nums[last]<nums[index]){
                    flag=!flag;
                    ++count;
                    last=index;
                }        
        return count;
    }
};
/*
        for(;index<nSize;++index)
            if(nums[last]<nums[index])
                if(flag)
                    last=index;
                else{
                    flag=!flag;
                    ++count;
                    last=index;
                }
            else if(nums[last]>nums[index])
                if(flag){
                    flag=!flag;
                    ++count;
                    last=index;
                }
                else
                    last=index; 
/*
//发现last永远是index-1,所以代码可改进
class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        int nSize=nums.size();
        if(nSize<2)return nSize;
        int index=1,count=2;
        while(index<nSize&&nums[index]==nums[0])//找第一个和nums[0]不同的值
            ++index;
        if(index==nSize)return 1;//如果都相同
        bool flag=nums[0]<nums[index];//1:找大数 0:找小数
        for(;index<nSize;++index)
            if(nums[index-1]<nums[index]&&!flag){
                flag=!flag;
                ++count;               
            }
            else if(nums[index-1]>nums[index]&&flag){
                flag=!flag;
                ++count;             
            }            
        return count;
    }
};

给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。

示例:

nums = [1, 2, 3]
target = 4

所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)

请注意,顺序不同的序列被视作不同的组合。

因此输出为 7。


进阶:
如果给定的数组中含有负数会怎么样?
问题会产生什么变化?
我们需要在题目中添加什么限制来允许负数的出现?(增加每个数的使用次数上限,否则可能会有无数种,a和-a出现的话)

class Solution {//回溯超时
public:
    int combinationSum4(vector<int>& nums, int target) {
        if(target<1||nums.size()==0)return 0;
        int nSize=nums.size();
        sort(nums.begin(),nums.end());
        int count=0;
        backTrack(nums,target,count);
        return count;
    }
    void backTrack(vector<int>& nums,int target,int &count){
        //if(target<0)return;
        if(target==0){
            ++count;
            return ;
        }
        for(int i=0;i<nums.size()&&target>=nums[i];++i){
            backTrack(nums,target-nums[i],count);
        }
    }
};
class Solution {//记忆化线索+回溯,依旧超时
//总结:一般结果是要统计数量(单个变量结果)的不建议用回溯法,用动态规划好
//一般结果是要具体的排列情况/组合情况等(二维数组)建议使用回溯法
public:
    vector<int> reversed;
    int combinationSum4(vector<int>& nums, int target) {
        if(target<1||nums.size()==0)return 0;
        int nSize=nums.size();
        sort(nums.begin(),nums.end());
        reversed.resize(target+1,0);        
        return backTrack(nums,target);
    }
    int backTrack(vector<int>& nums,int target){
        if(target==0)
            return 1;        
        if(reversed[target])          
            return reversed[target];        
        for(int i=0;i<nums.size()&&target>=nums[i];++i)
            reversed[target]+=backTrack(nums,target-nums[i]);
        return reversed[target];
    }
};
class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        if(target<1||nums.size()==0)return 0;
        int nSize=nums.size();
        sort(nums.begin(),nums.end());
        vector<unsigned int>dp(target+1,0);  //没搞懂为啥long long不行,测试过long和long long都是64位,int是32位,可推知系统为64位系统,
        dp[0]=1;      
        for(int i=1;i<=target;++i){
            for(int j=0;j<nSize;++j){
                if(nums[j]<=i)
                    dp[i]+=dp[i-nums[j]];
            }
        }
        return dp[target];
    }
};

如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列。

例如,以下数列为等差数列:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9

以下数列不是等差数列。

1, 1, 2, 5, 7

 

数组 A 包含 N 个数,且索引从0开始。数组 A 的一个子数组划分为数组 (P, Q),P 与 Q 是整数且满足 0<=P<Q<N 。

如果满足以下条件,则称子数组(P, Q)为等差数组:

元素 A[P], A[p + 1], ..., A[Q - 1], A[Q] 是等差的。并且 P + 1 < Q 。

函数要返回数组 A 中所有为等差数组的子数组个数。

 

示例:

A = [1, 2, 3, 4]

返回: 3, A 中有三个子等差数组: [1, 2, 3], [2, 3, 4] 以及自身 [1, 2, 3, 4]。

class Solution {//i,i+1,,,j-1,j=(i,,,j-1)+(i+1,,,j)+(i,,,j)-(i+1,,,j-1)
                //dp[i]=2*dp[i-1]+1-dp[i-2],表示以i下标元素为结尾的等差数列的子数组数列总个数
public:
    int numberOfArithmeticSlices(vector<int>& A) {//一维动态规划
        int aSize=A.size();
        if(aSize<=2)return 0;
        vector<int> dp(aSize,0);
        int resCount=0;
        for(int i=2;i<aSize;++i){
            if(A[i]-A[i-1]==A[i-1]-A[i-2])
                dp[i]=(dp[i-1]<<1)+1-dp[i-2];
            else
                resCount+=dp[i-1];
        }
        return resCount+dp[aSize-1];
    }
};
//官方解法:dp[i]=dp[i-1]+1,表示新增i元素时所增加的等差数列个数,resCount=全部dp[i]和
class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& A) {//常数动态规划
        int aSize=A.size();
        if(aSize<=2)return 0;
        int resCount=0;
        int dp1=0,dp2=0,temp;
        for(int i=2;i<aSize;++i){
            if(A[i]-A[i-1]==A[i-1]-A[i-2]){
                temp=dp2;                
                dp2=(dp2<<1)+1-dp1;   
                dp1=temp;            
            }            
            else{
                resCount+=dp2;
                dp1=0,dp2=0;
            }
                
        }
        return resCount+dp2;
    }
};

给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。

示例:

输入:[1,8,6,2,5,4,8,3,7]
输出:49
class Solution {
public:
    int maxArea(vector<int>& height) {
        int resMax=INT_MIN;
        int le=0,ri=height.size()-1;
        while(le<ri){
            resMax=max(resMax,(ri-le)*min(height[le],height[ri]));
            if(height[le]<height[ri])
                ++le;
            else
                --ri;
        }
        return resMax;
    }
};

给你一个区间列表,请你删除列表中被其他区间所覆盖的区间。

只有当 c <= a 且 b <= d 时,我们才认为区间 [a,b) 被区间 [c,d) 覆盖。

在完成所有删除操作后,请你返回列表中剩余区间的数目。

 

示例:

输入:intervals = [[1,4],[3,6],[2,8]]
输出:2
解释:区间 [3,6] 被区间 [2,8] 覆盖,所以它被删除了。


 

提示:​​​​​​


    1 <= intervals.length <= 1000
    0 <= intervals[i][0] < intervals[i][1] <= 10^5
    对于所有的 i != j:intervals[i] != intervals[j]

class Solution {
public:
    int removeCoveredIntervals(vector<vector<int>>& intervals) {
        if(intervals.empty())return 0;
        int mSize=intervals.size();
        if(mSize==1||intervals[0].empty())return 0;
        sort(intervals.begin(),intervals.end());
        int cur=0,deleteCount=0;
        for(int i=1;i<mSize;++i){
            if(intervals[i][1]>intervals[cur][1])
                cur=i;
            else
                ++deleteCount;
        }
        return mSize-deleteCount;
    }
};

给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

 

进阶:

你能在线性时间复杂度内解决此题吗?

 

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7] 
解释: 

  滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

 

提示:


    1 <= nums.length <= 10^5
    -10^4 <= nums[i] <= 10^4
    1 <= k <= nums.length

class Solution {//练习线段树
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        segTreeNode *seg=buildSegTree(nums,0,nums.size()-1);
        int nSize=nums.size();
        vector<int> res;
        for(int i=0;i<nSize-k+1;++i)
            res.push_back(getMaxVal(seg,nums,i,i+k-1));
        return res;
    }
    struct segTreeNode{
        int le,ri;
        segTreeNode *left,*right;
        segTreeNode(int le,int ri):le(le),ri(ri),left(nullptr),right(nullptr){};
        int maxVal;
    };
    segTreeNode *buildSegTree(vector<int> &nums,int le,int ri){
        if(le>ri)return nullptr;
        segTreeNode *seg=new segTreeNode(le,ri);
        if(le==ri){
            seg->maxVal=nums[le];
            return seg;
        }
        int mid=le+((ri-le)>>1);
        segTreeNode *segLeft=buildSegTree(nums,le,mid);
        segTreeNode *segRight=buildSegTree(nums,mid+1,ri);
        seg->left=segLeft;
        seg->right=segRight;
        seg->maxVal=max(segLeft->maxVal,segRight->maxVal);
        return seg;
    }
    int getMaxVal(segTreeNode *seg,vector<int> &nums,int le,int ri){
        if(le==seg->le&&ri==seg->ri)return seg->maxVal;
        int mid=seg->le+((seg->ri-seg->le)>>1);
        if(ri<=mid)return getMaxVal(seg->left,nums,le,ri);
        else if(le>mid)return getMaxVal(seg->right,nums,le,ri);
        else return max(getMaxVal(seg->left,nums,le,mid),getMaxVal(seg->right,nums,mid+1,ri));        
    }
};
class Solution {//单调队列 双端队列实现
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {        
        vector<int> res;
        int nSize=nums.size();
        deque<int>d;//存的是数组下标,出现比较大小<>=时要特别注意写错比较对象
        for(int i=0;i<k;++i){//进 滑动窗口前k个 
            while(d.size()&&nums[i]>=nums[d.back()])
                d.pop_back();
            d.push_back(i);
        }
        res.push_back(nums[d.front()]);
        for(int i=k;i<nSize;++i){
            if(i-k==d.front())//出 最后一个
                d.pop_front();
            while(d.size()&&nums[i]>=nums[d.back()])//进最新一个
                d.pop_back();
            d.push_back(i);
            res.push_back(nums[d.front()]);//取最值
        }
        return res;
    }
};
class Solution {//动态规划 left_right[i] right_left[i] 以k分块,块左块右最大值
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {        
        vector<int> res;
        int nSize=nums.size();
        vector<int>lTor(nSize);
        vector<int>rTol(nSize);
        lTor[0]=nums[0];
        rTol[nSize-1]=nums[nSize-1];
        for(int i=1,j=nSize-2;i<nSize;++i,--j){
            if(nums[i]%k==0)
                lTor[i]=nums[i];            
            else
                lTor[i]=max(lTor[i-1],nums[i]);
            if(nums[j+1]%k==0)
                rTol[j]=nums[j];            
            else
                rTol[j]=max(rTol[j+1],nums[j]);
        }
        for(int i=0;i<nSize-k+1;++i)
            res.push_back(max(lTor[i+k-1],rTol[i]));
        return res;
    }

};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值