《代码随想录》Ⅷ 贪心算法 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​ 中作为 摆动序列最长子序列的长度

一、思想

这道题的核心思想是使用贪心算法,通过记录摆动序列中的转折点来找到最长的摆动子序列。摆动序列的特点是相邻元素之间的差值在正负之间交替变化。我们可以通过遍历数组,记录每次差值变化的方向,从而确定摆动序列的长度。

具体来说,我们维护两个变量:prediff​(前一个差值)和curdiff​(当前差值)。如果prediff​和curdiff​的符号相反,说明出现了摆动,此时我们可以增加摆动序列的长度,并更新prediff​为curdiff​。通过这种方式,我们可以找到最长的摆动子序列。

二、代码

class Solution
{
public:
    int wiggleMaxLength(vector<int> &nums)
    {
        // 如果数组长度小于等于1,直接返回数组长度
        if (nums.size() <= 1) {
            return nums.size();
        }

        // 初始化当前差值为0,前一个差值为0,结果长度为1
        int curdiff = 0, prediff = 0, result = 1;

        // 遍历数组,计算当前差值和前一个差值
        for (int i = 0; i < nums.size() - 1; ++i) {
            curdiff = nums[i + 1] - nums[i];
            // 如果前一个差值小于等于0,当前差值大于0,或者前一个差值大于等于0,当前差值小于0,
            // 那么更新结果长度,并将当前差值赋值给前一个差值
            if ((prediff <= 0 && curdiff > 0) || (prediff >= 0 && curdiff < 0)) {
                result++;
                prediff = curdiff;
            }
        }
        // 返回结果长度
        return result;
    }
};

三、代码解析

1. 算法工作原理分解
1.1 初始化
  • 目的:处理特殊情况并初始化变量。

  • 实现

    • 如果数组长度小于等于1,直接返回数组长度。
    • 初始化prediff​(前一个差值)为0,curdiff​(当前差值)为0,result​(结果长度)为1。
1.2 遍历数组
  • 目的:计算当前差值并判断是否出现摆动。

  • 实现

    • 遍历数组,计算当前差值curdiff = nums[i + 1] - nums[i]​。
    • 如果prediff​和curdiff​的符号相反(即prediff <= 0 && curdiff > 0​或prediff >= 0 && curdiff < 0​),说明出现了摆动,此时增加result​,并更新prediff​为curdiff​。
1.3 返回结果
  • 目的:返回最终计算出的最长摆动子序列的长度。
  • 实现:返回变量result​的值。
2. 关键点说明
2.1 差值的计算与判断
  • 差值计算:通过nums[i + 1] - nums[i]​计算当前差值。
  • 摆动判断:如果prediff​和curdiff​的符号相反,说明出现了摆动。
2.2 结果的更新
  • 结果更新:每次出现摆动时,增加result​,并更新prediff​为curdiff​。
2.3 特殊情况的处理
  • 特殊情况:如果数组长度小于等于1,直接返回数组长度。

四、复杂度分析

  • 时间复杂度O(n)

    • 其中n​是数组的长度。算法只需要遍历数组一次,因此时间复杂度为O(n)​。
  • 空间复杂度O(1)

    • 除了输入数组外,算法只使用了常数级别的额外空间,因此空间复杂度为O(1)​。

白展堂:人生就是这样,苦和累你总得选一样吧?哪有什么好事都让你一个人占了呢。 ——《武林外传》

### 关于贪心算法的讲解 贪心算法是一种在每一步选择中都采取当前状态下最好或最优的选择,从而希望最终结果也是全局最优的一种算法策略[^1]。 对于某些特定问题而言,这种局部最优解能够直接导向全局最优解。然而,并不是所有的优化问题都能通过这种方法求得最精确的结果,但在很多情况下可以获得接近最优解的有效方案。 #### 示例一:最大和转换后的数组元素(Java) 考虑这样一个例子,在给定整数列表`nums`以及一个非负整数`k`的情况下,允许执行最多`k`次操作来改变任意数量的数值符号。目标是在不超过`k`次翻转的前提下最大化所有元素之和: ```java class Solution { public int largestSumAfterKNegations(int[] nums, int k) { Arrays.sort(nums); int count = 0; for (int i = 0; i < nums.length; i++) { if (k > 0 && nums[i] < 0) { nums[i] = -nums[i]; k--; } count += nums[i]; } Arrays.sort(nums); return count - ((k % 2 == 0) ? 0 : 2 * nums[0]); } } ``` 这段代码实现了上述逻辑,其中先对输入数组进行了升序排列以便优先处理负值较大的项,之后再根据剩余的操作次数决定是否调整最小正值以进一步提升总和[^2]。 #### 示例二:分配最少糖果数目 另一个典型的应用场景涉及向一群孩子分发糖果,条件是一个孩子的评分高于其左侧邻居,则该名学生应获得更多的糖果。这里采用了一种简单直观的方法——每当遇到更高的分数就增加一颗糖的数量直到遍历结束整个序列为止[^3]。 ```python def distribute_candies(ratings): n = len(ratings) candies = [1]*n for i in range(1,n): if ratings[i]>ratings[i-1]: candies[i]=candies[i-1]+1 for j in reversed(range(n-1)): if ratings[j]>ratings[j+1] and candies[j]<=candies[j+1]: candies[j]=candies[j+1]+1 return sum(candies) ``` 此Python函数展示了如何利用两次扫描过程分别从前至后和从后往前更新每个位置上的最低需求量,确保满足题目要求的同时使得总的糖果消耗达到最小化。 #### 示例三:寻找合适的起始站点完成环形路线旅行 最后来看一个更复杂的案例—解决“加油站”问题。假设存在一系列连的服务区构成闭合路径,车辆可以在任一站加油并继续行驶直至下一个目的地。为了判断能否顺利完成一圈旅程,可以通过计算各段行程结束后所剩燃油量来进行评估。具体做法是从第一个节点开始累积净增益(`gas-cost`),只要中途未曾跌入负区间即表明可以从起点出发成功返回原点;反之则需重新选定其他候选作为新的出发点尝试验证[^4]。 ```cpp bool canCompleteCircuit(vector<int>& gas, vector<int>& cost) { int total_tank = 0, curr_tank = 0, starting_station = 0; for (size_t i=0 ; i<gas.size() ; ++i){ total_tank += gas[i]-cost[i]; curr_tank += gas[i]-cost[i]; // If one couldn't get here, if(curr_tank < 0){ // Start over from next station. starting_station=i+1; curr_tank=0; } } return total_tank >= 0 && starting_station != gas.size(); } ``` 以上三个实例均体现了不同形式下的贪心思维模式及其应用技巧,它们共同之处在于总是倾向于做出当下看来最佳的动作,进而逐步构建出完整的解决方案框架。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值