376. Wiggle Subsequence
题目理解
给定一个数组,相邻两个数计算差值。差值排成的序列是正负相间的,那这个数组就是一个wiggle 数组。例如数组[1,7,4,9,2,5],差值序列是(6,-3,5,-7,3)。原数组用坐标轴表示如下。
思路是:在一段连续上升或者连续下降的线段上,那只保留两端的断点(这是贪心思想的体现),去掉中间的断点,就能使得子序列符合要求。例如在AB线段上,去掉中间点
A1,A2
A
1
,
A
2
,在BC线段上去掉
B1,B2
B
1
,
B
2
,那留下在子序列[A,B,C]就是符合要求的,并且是长度最长的wiggle子序列。
证明:要证明上面的思路是正确的。可以使用反证法。假如在一段连续上升的线段中不是保留最顶点B,而是留下
A2
A
2
点,删除一个点,添加一个点,总长度不发生变化。那会影响其他点吗?
A2<B
A
2
<
B
。B点之后是一段下降的线段。
<A2
<
A
2
<script type="math/tex" id="MathJax-Element-879">
<B
<
B
<script type="math/tex" id="MathJax-Element-880">
<B
<
B
<script type="math/tex" id="MathJax-Element-881">
<A2
<
A
2
<script type="math/tex" id="MathJax-Element-882">
A2
A
2
替换B没有任何好处。可以用同理分析下降线段中去掉中间点是最合理的。
学习:对于每一种解决方法是应该有证明的。此外,本题目还要考虑各种变化趋势,增加测试数据。
考虑:先直线再折线;先折线再直线;折线折线直线;折线直线折线。
public int wiggleMaxLength(int[] nums) {
if(nums.length<=1){
return nums.length;
}
int ans = 1;
int diff = nums[1]-nums[0];
if(diff!=0){
ans++;
}
for(int i=2;i<nums.length;i++){
if(nums[i]!=nums[i-1]){
if(diff * (nums[i]-nums[i-1])<=0){
ans++;
}
diff = nums[i]-nums[i-1];
}
}
return ans;
}