LeetCode-376-mid-摆动序列(贪心、动态规划、具体分析)

关键字: 贪心、动态规划、具体分析

题目描述

在一个数组中选出相对大小交替的最长子序列。
LeetCode-376-mid-摆动序列

分析

  • 摆动序列:对于非边界点,是峰顶 a r r [ i − 1 ] < a r r [ i ] > a r r [ i + 1 ] arr[i-1] < arr[i] > arr[i+1] arr[i1]<arr[i]>arr[i+1]或谷底 a r r [ i − 1 ] > a r r [ i ] < a r r [ i + 1 ] arr[i-1] > arr[i] < arr[i+1] arr[i1]>arr[i]<arr[i+1]
  • 最长摆动序列个数有很多,但是最长摆动序列的长度受限。
  • 生成式地考虑:对于一个已有的摆动序列,在不知道数组中下一个值arr[i+1]的情况下,如果此摆动序列目前为末尾上升序列,则其最后一个值越大,将arr[i+1]附加在该上升摆动序列末尾形成新的下降摆动序列的概率就越大。

(与动态规划解这道题相比)总的来说,本题的最佳解题方式为贪心:计数拐点个数即可。

解题思路(贪心)

对于摆动序列,每次末尾值的选择,如果当前是上升摆动序列,且下一个值arr[i+1]比摆动序列最后一个值还要大,则将摆动序列的末尾值更新为arr[i+1],结果仍然保持为上升摆动序列且长度不增加。如果下一个值更小,则长度增加,转变为下降摆动序列。

即为在寻找序列的峰值和谷底数目。

算法
INPUT: nums[]
OUTPUT: maxLen

if nums.size() < 1 :
    return nums.size()

arr <- nums 去重

isUp = (nums[1]>nums[0])
count = 1
for i = 2 to arr.size():
    if (nums[i]>nums[i-1]) ^ isUp :
        isUp = !isUp
        count += 1
maxLen = count + 1
return maxLen
数据结构

不需要特殊的读写等功能,线性表遍历即可。

复杂度分析

时间复杂度:去重(O(n))+遍历球峰顶与谷底数目(O(n)) = O(n)

空间复杂度:O(n)

代码实现
class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {

        //[0,0] 输出为1; 去除相邻重复
        if ( nums.size() < 1 )
            return 0 ;
        vector<int> arr ;
        arr.push_back( nums[0] ) ;
        for ( int i = 1 ; i < nums.size() ; ++ i ){
            if ( nums[i] == nums[i-1] ){
                continue ;
            }
            arr.push_back(nums[i]) ;
        }
        //count 为摆动次数
        if ( arr.size() < 2 ){
            return arr.size() ;
        }
        bool flag = (arr[1] > arr[0]) ;
        int count = 1;
        for ( int i = 2 ; i < arr.size() ; ++i ){
            if ( (arr[i] > arr[i-1]) ^ flag ){
                flag = !flag ;
                ++ count ;
            }
        }
        return count+1 ;
    }
};

解题思路(动态规划1)

up[i] 记录以i结尾的最长上升摆动序列
down[i] 记录以i结尾的最长下降摆动序列

对于k+1:
以k+1结尾的最长上升摆动序列:满足j < k+1且nums[j] < nums[k+1]的down[j] 的最大值+1产生
以k+1结尾的最长下降摆动序列:满足j < k+1且nums[j] > nums[k+1]的up[j] 的最大值+1产生

迭代公式:
u p [ i ] = { 1 i = 0 m a x ( d o w n [ 0 ] , d o w n [ 1 ] , . . . , d o w n [ j ] , . . . , d o w n [ i − 1 ] ) + 1 n u m s [ j ] < n u m s [ i ] up[i] = \begin{cases} 1 & i = 0 \\ max( down[0], down[1], ..., down[j], ..., down[i-1])+1 & nums[j]<nums[i] \end{cases} up[i]={1max(down[0],down[1],...,down[j],...,down[i1])+1i=0nums[j]<nums[i]

迭代公式:
d o w n [ i ] = { 1 i = 0 m a x ( u p [ 0 ] , u p [ 1 ] , . . . , u p [ j ] , . . . , u p [ i − 1 ] ) + 1 n u m s [ j ] > n u m s [ i ] down[i] = \begin{cases} 1 & i = 0 \\ max( up[0], up[1], ..., up[j], ..., up[i-1])+1 & nums[j]>nums[i] \end{cases} down[i]={1max(up[0],up[1],...,up[j],...,up[i1])+1i=0nums[j]>nums[i]

算法
INPUT: nums[]
OUTPUT: maxLen

if nums.size() < 1 :
    return nums.size()

arr <- nums 去重
up, down <- [1]

for i = 1 to arr.size() :
    for j = 0 to i-1 :
        if ( nums[i] > nums[j] )
            up[i] = max( up[i], down[j]+1 )
        if ( nums[i] < nums[j] )
            down[i] = max( down[i], up[j]+1 )

数据结构

不需要特殊的读写等功能,vector数组存储down[]和up[]即可。

复杂度分析

时间复杂度:去重( O ( n ) O(n) O(n))+遍历迭代( O ( n 2 ) O(n^2) O(n2))= O ( n 2 ) O(n^2) O(n2)

空间复杂度:O(n)

解题思路(动态规划2)

up[i] 记录前i个数中的最长上升摆动序列
down[i] 记录前i个数中的最长下降摆动序列

对于k+1:
前k+1个数中的最长上升摆动序列:若nums[k] < nums[k+1],则up[k+1]=max(up[k],down[k]+1); 否则 up[k+1] = up[k]
前k+1数中的最长下降摆动序列:若nums[k] > nums[k+1],则down[k+1]=max(down[k],up[k]+1); 否则 down[k+1] = down[k]

迭代公式:
u p [ i ] = { m a x ( u p [ i − 1 ] , d o w n [ i − 1 ] + 1 ) n u m s [ i ] > n u m s [ i − 1 ] u p [ i − 1 ] n u m s [ i ] < = n u m s [ i − 1 ] up[i] = \begin{cases} max(up[i-1], down[i-1]+1) & nums[i] > nums[i-1] \\ up[i-1] & nums[i] <= nums[i-1] \end{cases} up[i]={max(up[i1],down[i1]+1)up[i1]nums[i]>nums[i1]nums[i]<=nums[i1]

d o w n [ i ] = { m a x ( d o w n [ i − 1 ] , u p [ i − 1 ] + 1 ) n u m s [ i ] < n u m s [ i − 1 ] d o w n [ i − 1 ] n u m s [ i ] > = n u m s [ i − 1 ] down[i] = \begin{cases} max(down[i-1], up[i-1]+1) & nums[i] < nums[i-1] \\ down[i-1] & nums[i] >= nums[i-1] \end{cases} down[i]={max(down[i1],up[i1]+1)down[i1]nums[i]<nums[i1]nums[i]>=nums[i1]

算法
INPUT: nums[]
OUTPUT: maxLen

if nums.size() < 1 :
    return nums.size()

arr <- nums 去重
up, down <- [1]

for i = 1 to arr.size() :
    if arr[i] > arr[i-1] :
        up[i] = max(up[i-1], down[i-1]+1)
        down[i] = down[i-1]
    if arr[i] < arr[i-1] :
        up[i] = up[i-1] 
        down[i] = max(down[i-1], up[i-1]+1)

return max( up[-1], down[-1])
数据结构

不需要特殊的读写等功能,vector数组存储down[]和up[]即可。

复杂度分析

时间复杂度:去重( O ( n ) O(n) O(n))+遍历迭代( O ( n ) O(n) O(n))= O ( n ) O(n) O(n)

空间复杂度:O(n)

相关问题

LeetCode-300-mid-求最长上升子序列,这道题是使用动态规划来求解,但是在题面上很相似。
在摆动序列这道题中,有文章指出这一道题目。但是该题的动态规划算法对应这里给出动态规划1(本文的第二个算法)。第二个版本的动态规划实际上用了贪心的部分来减少计算量。


PS.

  1. 相比较于其他已有的leetcode刷题笔记,我希望能够提供相关的解题分析和部分相关问题的链接,使得大家能获得一个较好的分析与相关问题的比较。

  2. 偶尔会进行类似题目的总结工作,有需要的读者可以对这份工作进行关注。

  3. 如果你发现有相关的错误或者表述不当不清晰的地方,请进行评论,我会尽快进行核对勘正。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值