笔记87:Leetcode_376_摆动序列

前言:最开始写的时候并没有写出来,代码随想录提供的答案也很令人费解,个人感觉很不直观,也很不好理解,思路并不连贯;我根据代码随想录的启发提供另一种更好理解的思路;


代码随想录解法:

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        //剪枝:
        if(nums.size() == 0) return 0;
        if(nums.size() == 1) return 1;

        int curDiff = 0; //当前点的前方差值
        int preDiff = 0; //当前点的后方差值
        int result = 1;  //记录峰值个数(默认序列结尾处有一个峰值)

        for (int i = 0; i < nums.size() - 1; i++) {
            curDiff = nums[i + 1] - nums[i];
            //出现峰值:
            if ((preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0)) {
                result++;
                preDiff = curDiff;      //注意这里,只在出现峰值的时候更新prediff
            }
        }

        return result;
    }
};

我的解法:

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        //剪枝:
        if(nums.size() == 0) return 0;
        if(nums.size() == 1) return 1;
        if(nums.size() == 2 && nums[0] == nums[1]) return 1;
        if(nums.size() == 2 && nums[0] != nums[1]) return 2;

        int ptr = 1;
        while(ptr < nums.size() - 1) {
            if(nums[ptr] == nums[ptr - 1] || nums[ptr] == nums[ptr + 1])        //存在想等元素
                nums.erase(nums.begin() + ptr);
            else if(nums[ptr] > nums[ptr - 1] && nums[ptr + 1] > nums[ptr])     //单调减
                nums.erase(nums.begin() + ptr);
            else if(nums[ptr] < nums[ptr - 1]  && nums[ptr + 1] < nums[ptr])    //单调增
                nums.erase(nums.begin() + ptr);
            else ptr++;
        }

        if(nums.size() == 2 && nums[0] == nums[1]) return 1;        //排除特殊情况:序列中所有的元素均相同
        return nums.size();
    }
};

思路1:针对题目,我一般都是先对应着一般的情况去写,然后再补充特殊情况;根据图片可知,如果我们想得到最长的摇摆子序列,那必须保留所有的摆动峰,因此单调增/单调减/保持不变的所有值,都是多余的,都可以直接删掉而不影响摆动峰的数目;删除所有的多余值以后,nums的长度就是子序列的最长长度;

a

a

a

思路2:我们先不考虑针对nums的开头位置/结尾位置的处理,我们先写程序解决对nums中央部位的元素的处理;对所有可能的9种情况(先增 / 先降 / 先平)进行分析,将所有不可能进入子序列的节点全部删除;

  • 其中一边有相同元素,则删除当前节点
  • 元素位于单调增的中间,删除元素
  • 元素位于单调减的中间,删除元素

写出主体代码:在写的时候注意while循环体的判定条件,因为我们在主程序中对ptr指向位置进行判定的时候,需要ptr两边的元素,所以我们为了在结尾位置能对应上,需要保留一个元素位;

        int ptr = 1;
        while(ptr < nums.size() - 1) {
            if(nums[ptr] == nums[ptr - 1] || nums[ptr] == nums[ptr + 1])        //存在想等元素
                nums.erase(nums.begin() + ptr);
            else if(nums[ptr] > nums[ptr - 1] && nums[ptr + 1] > nums[ptr])     //单调减
                nums.erase(nums.begin() + ptr);
            else if(nums[ptr] < nums[ptr - 1]  && nums[ptr + 1] < nums[ptr])    //单调增
                nums.erase(nums.begin() + ptr);
            else ptr++;
        }

a

a

a

思路3:考虑nums开头部分的节点;我们可以发现我们对数组nums中间部分的节点的处理方式,和对nums开头位置的节点的处理方式是一样的,所以主体代码可以不用改动;

a

a

a

思路4:考虑对nums尾部的节点的处理方式;以“先增”情况为例:

分析:红色位置是当前循环体中ptr指向的位置,而ptr所指向的节点的前一个节点(绿色节点)的前方,一定是下降趋势;因为当ptr到达当前红色位置的时候,代表他前方所有的节点都已经被遍历并被处理过了,所以ptr的前方不存在对摆动毫无作用的点,所以当绿点到红点的趋势为增时,代表绿点前的趋势一定为降,绝不可能是单增/单降/单平;

分析:当ptr处理完当前红色节点后,就会跳出循环体,而留下的紫色节点并不需要进行判断,他们一定是符合摆动的节点,一定可以被纳入到子序列中;

分析:那么同理“先降”情况也是相同的分析过程;

分析:但是“先平”却存在特殊情况;当ptr指向绿色位置的时候就会删除这些绿色节点,这些绿色节点的前面的趋势可能是上升/下降(不可能是平),再指向红色节点对红色节点进行处理,第一种和第三种情况都是符合要求的;但是对于第二种情况,当结尾部分的节点都是平,那么当ptr指向红色位置的时候会删除红色节点,然后跳出while循环;但是此时留下的紫色节点却和前方的节点是“平”的关系。没有被删除掉;所以我们在跳出while循环后仍然要对nums的尾部进行判断;

加入代码:

if(nums.size() == 2 && nums[0] == nums[1]) return 1;        //排除特殊情况:序列中所有的元素均相同
return nums.size();
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。它的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其中next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式串的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本串为S="ababababca",模式串为P="abababca",我们想要在S中查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法的C++代码实现:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值