leetcode_376. Wiggle Subsequence(DP 和 Greedy)

来源:互联网 发布:数组.length 编辑:程序博客网 时间:2024/06/18 15:19

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

DP

题意:
给定一个序列,找出其中最长的子序列,使得该子序列相邻两个数的差值(后一个减去前一个)形成的序列正负交替(不能为0)。

分析:

  1. 该题可以用动态规划(DP)解决,我们作如下考虑:假设以i为最后一个元素的子序列中,wiggle sequence的最后一个值为负数的最优解为dec[i],最后一个值为整数的最优解为inc[i],则最优解max[i] = max{inc[i], dec[i]}。
  2. 下面分析最优子结构性质,对于dec[i],该子序列的前一个元素为j(j< i & nums[i] < nums[j]),则inc[j] = dec[i] - 1,若不然,设存在以j为最后一个元素且wiggle sequence中最后一个数为正数的子序列S个数为m, m > dec[i] - 1,则序列S + nums[i] 满足wiggle sequence,即存在以i为最后一个元素且wiggle sequence中最后一个数为负数的子序列S + nums[i],个数为 m + 1 > inc[j] + 1 = dec[i],这以假设dec[i]为以i为最后一个元素且wiggle sequence中最后一个数为负数的最优解矛盾。inc[i]的证明类似。

递推公式:

dec[i]=max(inc[j]+1)j<i,nums[i]<nums[j]

inc[i]=max(dec[j]+1)j<i,nums[i]>nums[j]

max[i]=max(dec[i],inc[i])

max=max(max[i],0<=i<=n)

代码(c++, O(N2)):

class Solution {public:    int wiggleMaxLength(vector<int>& nums) {        vector<int> inc(nums.size(), 1);        vector<int> dec(nums.size(), 1);        int maxs = (nums.size() == 0 ? 0 : 1);        for(int i = 1; i < nums.size(); i++){            for(int j = 0; j < i; j++){                if(nums[i] > nums[j] && dec[j] + 1 > inc[i]) inc[i] = dec[j] + 1;                if(nums[i] < nums[j] && inc[j] + 1 > dec[i]) dec[i] = inc[j] + 1;            }            maxs = max(maxs, max(inc[i], dec[i]));        }        return maxs;    }};

Greedy

后来看了一下题目最后有一句话:Can you do it in O(n) time? 上面的DP虽然可以过,但是时间复杂度为O(N2),于是想了一个别的方法。

分析:

我们可以这样分析:假设合法的子序列中第一个数为正数(> 0),设该序列为[x1,x2,x3,x4,...,xk],满足nums[x2]nums[x1]>0,nums[x4]<nums[x3]<0...(nums[xk]nums[xk1])(1)logk+1>0,所以我们需要一个方向位ai来记录下一个nums[i] - nums[j]应该大于0还是小于0。可以使用贪心策略:从前往后选择合法的数加入子序列中,如果不合法,则将解序列中的最后一个数替换为当前的数。

证明:

  1. 假如部分子序列[x1,x2,x3,x4,...,xm]中,nums[x_m] > nums[x_{m-1}],那么接下来添加的数必须满足nums[x_i] < nums[x_m],如果不满足,则用i替换xm,替换后的序列为[x1,x2,x3,x4,...,xm1,i],因为nums[i] > nums[xm],所以nums[i] > nums[xm1],替换后的序列合法,
  2. 下面证明如果最优序列S中含有[x1,x2,x3,x4,...,xm],一定也存在一个最优序列S’,包含[x1,x2,x3,x4,...,xm1,i],假设 j为S中第一个下标比xm大的数的下标,S = [x1,x2,x3,x4,...,xmj]  S1,用i替换xmnums[j]<nums[x_m]<nums[i]S=[x_1,x_2,x_3,x_4,…,x_m,i]  S1合法,且|S’| = |S|,所以S’也是最优解。

代码(O(N)):

class Solution {public:    int wiggleMaxLength(vector<int>& nums) {        int firstinc, firstdec;        if(nums.size() == 0) return 0;        firstinc = firstdec = 1;        int ai, bi;        ai = 1, bi = -1; //初始化,ai交替取1和-1,ai*(nums[i] - nums[inc])用于判断差值是否正负交替        for(int i = 1; i < nums.size(); i++){            if(ai * (nums[i] - nums[i-1]) > 0){//合法                ai = -ai; //换方向                firstinc++;            }            if(bi * (nums[i] - nums[i-1]) > 0){//合法                bi = -bi;                firstdec++;            }        }        return max(firstinc, firstdec);    }};
0 0
原创粉丝点击