动态规划-376-摆动子序列

Description:

A sequence of numbers is called a wiggle sequence if the differences between successive numbers strictly alternate between positive and negative. The first difference (if one exists) may be either positive or negative. A sequence with fewer than two elements is trivially a wiggle sequence.

For example, [1,7,4,9,2,5] is a wiggle sequence because the differences (6,-3,5,-7,3) are alternately positive and negative. In contrast, [1,4,7,2,5] and [1,7,4,5,5] are not wiggle sequences, the first because its first two differences are positive and the second because its last difference is zero.

Given a sequence of integers, return the length of the longest subsequence that is a wiggle sequence. A subsequence is obtained by deleting some number of elements (eventually, also zero) from the original sequence, leaving the remaining elements in their original order.


Examples:

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

Follow up:
Can you do it in O(n) time?


问题描述:

如果一个序列中连续的数之间的差值在正数和负数之间来回交替,那么这个序列被称作摆动序列。如果存在第一个差值的话,可能为正数或者负数。长度小于2的序列被认为是摆动序列。
例如 [1,7,4,9,2,5]为摆动序列。而[1,4,7,2,5] 和[1,7,4,5,5]不是摆动序列
给定一组数,返回最长的摆动子序列。注意序列是由原数组删除一些元素(也可以不删除)并且保留元素顺序得到的。


问题分析:

这题大致有两种解法,贪心和动态规划
如果想详细了解这两种解法是如何实现的,建议看下这个链接,讲得非常清楚:
https://leetcode.com/problems/wiggle-subsequence/solution/

1.对于贪心,举个例子
[1, 2, 5, 7, 5, 4, 3, 5]
最长摆动子序列为[1, 7, 3, 5],长度为4
选择的原则是先将位于1处看作”下降”(针对这个例子,也可以是上升),然后在上升中找最大值,到了最大值后,下个元素从下降中的最小元素找,依次类推

2.对于动规,关键是两个状态和对应的转换

状态
1. up[i],位于i处,处于上升所能获得的最大摆动子序列长度
2. down[i],位于i处,处于下降所能获得的最大摆动子序列长度

转换
1. 当nums[i] > nums[j] && i > j,up[i] = down[j] + 1
2. 当nums[i] < nums[j] && i > j,down[i] = up[j] + 1

注意这里的动规是一个最原始的版本,还可以对其扩展,详细的话最好仔细看一下链接的文章


以下列举一些关键的解法

解法1(naive dp):

public class Solution {
    public int wiggleMaxLength(int[] nums) {
        if (nums.length < 2)
            return nums.length;
        int[] up = new int[nums.length];
        int[] down = new int[nums.length];
        for (int i = 1; i < nums.length; i++) {
            for(int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    up[i] = Math.max(up[i],down[j] + 1);
                } else if (nums[i] < nums[j]) {
                    down[i] = Math.max(down[i],up[j] + 1);
                }
            }
        }
        return 1 + Math.max(down[nums.length - 1], up[nums.length - 1]);
    }
}

解法2(扩展后的线性dp):

public class Solution {
    public int wiggleMaxLength(int[] nums) {
        if (nums.length < 2)
            return nums.length;
        int[] up = new int[nums.length];
        int[] down = new int[nums.length];
        up[0] = down[0] = 1;
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] > nums[i - 1]) {
                up[i] = down[i - 1] + 1;
                down[i] = down[i - 1];
            } else if (nums[i] < nums[i - 1]) {
                down[i] = up[i - 1] + 1;
                up[i] = up[i - 1];
            } else {
                down[i] = down[i - 1];
                up[i] = up[i - 1];
            }
        }
        return Math.max(down[nums.length - 1], up[nums.length - 1]);
    }
}

解法3(空间优化后的dp):

public class Solution {
    public int wiggleMaxLength(int[] nums) {
        if (nums.length < 2)
            return nums.length;
        int down = 1, up = 1;
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] > nums[i - 1])
                up = down + 1;
            else if (nums[i] < nums[i - 1])
                down = up + 1;
        }
        return Math.max(down, up);
    }
}

解法4(贪心):

public class Solution {
    public int wiggleMaxLength(int[] nums) {
        if (nums.length < 2)
            return nums.length;
        int prevdiff = nums[1] - nums[0];
        int count = prevdiff != 0 ? 2 : 1;
        for (int i = 2; i < nums.length; i++) {
            int diff = nums[i] - nums[i - 1];
            if ((diff > 0 && prevdiff <= 0) || (diff < 0 && prevdiff >= 0)) {
                count++;
                prevdiff = diff;
            }
        }
        return count;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值