力扣 376. 摆动序列 精讲

力扣 376. 摆动序列 精讲

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

  • 例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。

  • 相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。

给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。

示例 1:

输入:nums = [1,7,4,9,2,5]
输出:6
解释:整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3) 。

示例 2:

输入:nums = [1,17,5,10,13,15,10,5,16,8]
输出:7
解释:这个序列包含几个长度为 7 摆动序列。
其中一个是 [1, 17, 10, 13, 10, 16, 8] ,各元素之间的差值为 (16, -7, 3, -3, 6, -8) 。

示例 3:

输入:nums = [1,2,3,4,5,6,7,8,9]
输出:2

提示:

1 <= nums.length <= 1000
0 <= nums[i] <= 1000

方法一:贪心算法

可以理解为我只要这个数组中的波峰和波谷 即为总长度 且这俩交替出现就行 可以贪心的去省略中间上升到波峰的趋势的那些数字,也可以贪心的去省略下降到波谷的趋势。

具体的可以看注释,还看不懂就看这个

class Solution {
    public int wiggleMaxLength(int[] nums) {
        if(nums.length<=1){
            return nums.length;
        }
        //贪心算法
        //结果是初始的时候就算上了最左侧的  然后自增的时候就是俩了
        int res = 1;
        //记录上一次加入到序列中的节点的和后一个节点的差值
        int pre = 0;
        //记录当前节点和后一个节点的差值
        int cur=0;
        for(int i=0;i<nums.length-1;i++){
            //去看每一个峰值
            //就算满足了条件  也会一直更新 即找到局部最大或最小的去更新
            cur = nums[i+1]-nums[i];
            //每次局部峰值只会++一次 你可以理解为峰值(pre和cur正负不一致)就记录
            if(cur>0&&pre<=0||cur<0&&pre>=0){
                res++;
                pre=cur;
            }
        }  
        return res;
    }
}

方法二:dp 时间O(N2) 空间O(N)

具体的可以看注释,还看不懂就看这个

class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n=nums.length;
        if(n<=1){
            return n;
        }
        //dp算法
        //关键就在于从前面的序列转移过来的方式
        //而官方题解给出了O(n)的方式 感觉证明我不太理解
        //这里代码随想录只给出了O(N方)的dp


        //定义一个最后结尾为峰(即最后两个数是上升的序列)的dp数组
        //up[i]代表从0~i组成的最长的子序列长度且序列最后两个数是递增的
        int[] up = new int[n];

        //定义一个最后结尾为谷(即最后两个数是下降的序列)的dp数组
        //down[i]代表从0~i组成的最长的子序列长度且序列最后两个数是递减的
        int[] down = new int[n];

        //初始值都是1
        up[0]=1;
        down[0]=1;

        //遍历nums
        for(int i=1;i<n;i++){
            //对于每一个值都要和前面所有的值比较  得到最大长度的可能性
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]){
                    //当当前值大于序列中的值时 长度只可能从down[j]中增加
                    //即将nums[i]作为峰跟在nums[j]这个谷后面
                    up[i] = Math.max(up[i],down[j]+1);
                    //我虽然没你大 但是我作为个更长的序列 我应当保存最大的值
                    //因此这个更新没啥意义。。。
                    //从下面就更新好了
                    // down[i] = Math.max(down[i],down[j]);
                }else if(nums[i]<nums[j]){
                    //当当前值小于序列中的值时 长度只可能从up[j]中增加
                    //即将nums[i]作为谷跟在nums[j]这个峰后面
                    down[i] = Math.max(down[i],up[j]+1);
                    //我虽然没你大 但是我作为个更长的序列 我应当保存最大的值
                    //因此这个更新没啥意义。。。
                    //从上面就更新好了
                    // up[i] = Math.max(up[i],up[j]);
                }else{
                    up[i] = Math.max(up[i],up[j]);
                    down[i] = Math.max(down[i],down[j]);
                }
            }
        }        
        //因为最后一个值要么是波峰 要么是波谷 一定在最后一个位置上
        return Math.max(up[n-1],down[n-1]);
    }
}

方法三:dp 时间O(N) 空间O(N)

注意 dp数组的定义变了哦~

具体的可以看注释,还看不懂就看这个

class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n=nums.length;
        if(n<=1){
            return n;
        }
        //dp算法
        //而官方题解给出了O(n)的方式 差不多看懂了
        //关键在于当只通过和前一个值进行比较 就能得出前面一定是递减到一个谷这个结论


        //up[i]代表从0~i中某一个数为结尾的最长摆动序列的长度 且最后为递增的两个数
        int[] up = new int[n];

        //down[i]代表从0~i中某一个数为结尾的最长摆动序列的长度 且最后为递减的两个
        int[] down = new int[n];

        //初始值都是1
        up[0]=1;
        down[0]=1;

        //遍历nums
        for(int i=1;i<n;i++){
            //对于每一个值都要和前面所有的值比较  
            if(nums[i]<nums[i-1]){
                //当当前值小于前一个值时
                //对于up[i] 结尾处是递增的 而当前值小于最高处 因此0~i中的最长上摆序列依旧不会变 因为对于谷而言 i-1和i都一样(up[i-1]一致)
                up[i] = up[i-1];
                //对于down[i] 因为当前值小于前一个 此处一定是一个下降的趋势 前面一定有一个峰 那么我有可能是已经在递减的路上了(和down[i-1]一致 那么这种时候down[i-1]一定是最长的) 也有可能是第一个递减的数字(从up[i-1]+1来  代表从上摆序列到下摆序列的过程)
                down[i] = Math.max(down[i-1],up[i-1]+1);
            }else if((nums[i]>nums[i-1])){
                //当当前值大于前一个值时 
                //对于up[i] 结尾处是递增的 而当前值大于最高处因此可以从up[i-1]来,也可以从down[i-1]+1来 因为我大于前面一个值  因此前面的数组一定是一个递增的过程,那么前面第一个一定是谷而不是峰,那么我可以从前面的谷+1 从下摆转到上摆序列来 
                up[i] = Math.max(up[i-1],down[i-1]+1);
                //对于down[i] 我大于前一个值 那么对于总体的0~i而言 我肯定去nums[i-1]位置上的值更好  因为我要的是下摆序列 结尾一定要下降的 因此还是down[i-1]
                down[i]=down[i-1];
            }else{
                //相等的时候  直接和上一个一样
                up[i] = up[i-1];
                down[i] = down[i-1];
            }
        }        
        //最长序列一定在两个数组最后
        return Math.max(up[n-1],down[n-1]);
    }
}

方法四:dp 时间O(N) 空间O(1)

空间优化 没啥技术含量

具体的可以看注释,还看不懂就看这个

class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n=nums.length;
        if(n<=1){
            return n;
        }
        //dp算法
        //优化了空间  
        //如果你又看到这个题了 一定要看看上个版本的详细注释
        //注释写的很清楚了捏 这个是时间O(n) 空间O(1)的算法
        //上个是时间O(n) 空间O(n)
        //代码随想录的是时间O(n方) 空间O(n)
        int up = 1;
        int down = 1;
        for(int i=1;i<n;i++){
            if(nums[i]<nums[i-1]){
                down = Math.max(down,up+1);
            }else if((nums[i]>nums[i-1])){
                up = Math.max(up,down+1);
            }else{
            }
        }        
        //最长序列一定在两个数组最后
        return Math.max(up,down);
    }
}

方法五:dp 究极优化

来自数学的优化。。。每次出现趋势变动的时候就会+1 那么不用max函数了就

具体的可以看注释,还看不懂就看这个

class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n=nums.length;
        if(n<=1){
            return n;
        }
        //dp算法
        //优化了空间  
        //如果你又看到这个题了 一定要看看上个版本的详细注释
        //注释写的很清楚了捏 这个是时间O(n) 空间O(1)的算法
        //上个是时间O(n) 空间O(n)
        //代码随想录的是时间O(n方) 空间O(n)
        int up = 1;
        int down = 1;
        for(int i=1;i<n;i++){
            if(nums[i]<nums[i-1]){
                down = up+1;
            }else if((nums[i]>nums[i-1])){
                up = down+1;
            }
        }        
        //最长序列一定在两个数组最后
        return Math.max(up,down);
    }
}
posted @ 2022-09-14 11:13  杀戒之声  阅读(64)  评论(0编辑  收藏  举报