题目大意:
给定一个数组,让我们求解该数组的最大摆动子序列。何谓摆动子序列呢,就是一个大一个小,或者用相邻差值来说的话就是一个正数一个负数这样的序列就是摆动序列。
例子:
Input: [1,7,4,9,2,5] Output: 6 The entire sequence is a wiggle sequence. Input: [1,17,5,10,13,15,10,5,16,8] Output: 7 There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8]. Input: [1,2,3,4,5,6,7,8,9] Output: 2
算法:
一、DP算法
首先,我们来分析一下这道题目的子序列长度加一的条件是什么。一种条件就是如果当前子序列的差值为正,那么下一个数字必须小于当前子序列的最后一个数字才可以使得子序列的长度加一。另一种条件就是如果当前子序列的差值为负,那么下一个数字必须大于当前子序列的最后一个数字才可以加一。我们维护两个数组p[i]和q[i],p[i]代表当前数组遍历到第i位时,第一个差值为正数的摆动子序列的最大长度。同理,q[i]代表第一个差值为负数的摆动子序列的最大长度。状态转移方程就是对于位置小于当前位置的每一个元素考虑选择当前子序列长度和与新的状态较大的那一个。p[i]=max[p[i],q[j]+1]与q[i]=max[q[i],p[j]+1]是其状态转移方程。这种算法的复杂度为O(n^2)。
二、贪心算法
这种算法就是考虑最贪心的算法。我们考虑这个序列的最长子序列,如果遇到递增的一段的时候,我们就要考虑将这递增的一段的最后一个元素,即最大元素加入到子序列当中。因为选择了最大的,就给我们后面选择数字添加到子序列当中提供了更多的选择。同理,如果是要选择小于前一个数字的,且接下去的数字都是递减的,那么我们就挑选最小的添加到子序列当中。在这里面,我们维护一个flag,如果下一个要选择大于前一个数的值,我们就将flag立为1,已经挑选到我们就将flag立为-1代表要找比前一个数字小的数了。这个时候,如果后面还是递增的,我们就没必要再去改变flag的值了。这样就做到了忽略递增数列,整个递增数列只会为子序列提供一个长度。同理可知另一种情况。贪心算法的复杂度为O(n)。
代码:
class Solution
{
public:
int wiggleMaxLength(vector<int>& nums)
{
if (nums.size()==0) return 0;
vector<int> p(nums.size(), 1);
vector<int> q(nums.size(), 1);
for (int i = 1; i < nums.size(); ++i)
{
for (int j = 0; j < i; ++j)
{
if (nums[i] > nums[j]) p[i] = max(p[i], q[j] + 1);
else if (nums[i] < nums[j]) q[i] = max(q[i], p[j] + 1);
}
}
return max(p[nums.size()-1], q[nums.size()-1]);
}
};
贪心算法:
class Solution { public: int wiggleMaxLength(vector<int>& nums) { int answer=1; int n=nums.size(); if(n<2) return n; int i=1; int ff=0; while(i<=n-1) { if(nums[i]==nums[i-1]) i++; else if(nums[i]>nums[i-1]) { if(ff!=1) { answer++; ff=1; } i++; } else { if(ff!=-1) { answer++; ff=-1; } i++; } } return answer; } };