如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。
例如, [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;
}
};