题目:跳转至 376. 摆动序列
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。
例如, [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 {
public:
int wiggleMaxLength(vector<int>& nums) {
}
};
思路:
先随便发散一下思维,首先,摆动序列定义中连续数字差严格在正负数间交替,那如果出现0必然会分为两段,在此基础上要保证两两差值之间是正负交替的话,其乘积必大于0。分割两段表述有点问题,题目最后一段说明是可以删去部分原始数组的元素。(这段可以略过没用)
或者如何保证两两差值交替正负呢?不会出现递增或者递减就行。
如果出现递增序列,如[3,1,2,3,4,3]中[1,2,3,4]很明显递增,去除中间的[2,3]那[3,1,4,3]就成了一个摆动序列,因为递增的起点必小于前一个元素,顶点必大于后一个元素。
真的暴力破解,刚开始没有考虑到[1,1,1,1,1,1]和[1,1,1,7,3,5,1]这种,提交连连报错。终于搞定了,成功之后赶忙看题解,沉默是今晚的康桥,62行那么长情何以堪。
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int len=nums.size();
if(len==0)
return 0;
int maxn=INT_MIN;int minn=INT_MAX;
for(auto x:nums){
maxn=max(maxn,x);
minn=min(minn,x);
}
if(maxn==minn)
return 1;
vector<pair<int,int>> saveIncrease; //pair<起点下标,长度>
vector<pair<int,int>> saveDecrease;
int lastDiff=nums[1]-nums[0];
int i=1;
while(i<len){
//如果连续数字相同那只要保留一位就可以,其余都去掉
int count=0;
while(i<len && nums[i-1]==nums[i]){
++count;
++i;
}
if(count>=1 && i<len && nums[i-1]<nums[i])
saveIncrease.emplace_back(make_pair(i-count-1,count));
else if(count>=1 && i<len && nums[i-1]>nums[i])
saveDecrease.emplace_back(make_pair(i-count-1,count));
//如果是递增(包含相等)过程中,需要保留头和尾两处值
count=0;
while(i<len && nums[i-1]<=nums[i]){
++count;
++i;
}
if(count>1)
saveIncrease.emplace_back(make_pair(i-count,count-1));
//如果是递减(包含相等)过程中,同样需要保留头和尾两处值
int count1=0;
while(i<len && nums[i-1]>=nums[i]){
++count1;
++i;
}
if(count1>1)
saveDecrease.emplace_back(make_pair(i-count1,count1-1));
if(count==0 && count1==0)
++i;
}
for(int i=0;i<saveIncrease.size();++i){
for(int j=saveIncrease[i].first;j<saveIncrease[i].first+saveIncrease[i].second;++j)
nums[j]=INT_MAX;
}
for(int i=0;i<saveDecrease.size();++i){
for(int j=saveDecrease[i].first;j<saveDecrease[i].first+saveDecrease[i].second;++j)
nums[j]=INT_MAX;
}
int res=0;
for(auto x:nums){
if(x<INT_MAX)
++res;
}
return res;
}
};
题解两种方法:动态规划、贪心。个人觉得贪心好理解一点,和原本思路也相近,题解是只统计了数量。
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int n = nums.size();
if (n < 2) {
return n;
}
int prevdiff = nums[1] - nums[0];
int ret = prevdiff != 0 ? 2 : 1; //把开头就连续的情况考虑到
for (int i = 2; i < n; i++) {
int diff = nums[i] - nums[i - 1];
if ((diff > 0 && prevdiff <= 0) || (diff < 0 && prevdiff >= 0)) { //峰谷必须交替且差值不能为0即连续数相等
ret++;
prevdiff = diff;
}
}
return ret;
}
};
动态规划想想硬着头皮也看一下吧,
up[i](down[i])分别存储以前i个元素中的某一个为结尾的最长的上升(下降)摆动序列的长度。
以up[i]为例:
nums[i]<=nums[i-1]时,这是一个下降(或相等)过程,因为没有上升必然只能up[i]=up[i-1];
nums[i]>nums[i-1]时,这是一个上升过程,先最长下降摆动序列加一(down[i-1]+1),再放入最长上升摆动序列与原本值up[i-1]比较取最大,up[i]=max(up[i-1],down[i-1]+1);
down[i]同理。
最终的答案为up[n-1]和down[n−1]中的较大值,其中n是序列的长度。
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int n = nums.size();
if (n < 2) {
return n;
}
vector<int> up(n), down(n);
up[0] = down[0] = 1;
for (int i = 1; i < n; i++) {
if (nums[i] > nums[i - 1]) {
up[i] = max(up[i - 1], down[i - 1] + 1);
down[i] = down[i - 1];
} else if (nums[i] < nums[i - 1]) {
up[i] = up[i - 1];
down[i] = max(up[i - 1] + 1, down[i - 1]);
} else {
up[i] = up[i - 1];
down[i] = down[i - 1];
}
}
return max(up[n - 1], down[n - 1]);
}
};