代码随想录算法训练营day28

7 阅读4分钟

376摆动序列 1.理解摆动序列的判别条件 2.注意均为0的特殊情况,所以在更新前后差值时需要注意特殊条件


class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
    // 提前剪枝:数组长度≤1时直接返回自身长度
        if(nums.size() <= 1) return nums.size();
        
        int pre_diff = 0;  // 上一个非0的差值
        int cur_diff = 0;
        int result = 1;    // 至少有1个元素
        
        for (int i = 0; i < nums.size() - 1; i++) {
            cur_diff = nums[i+1] - nums[i];
            
            // 仅当cur_diff≠0,且与pre_diff异号时,才统计摆动
            if ((pre_diff >= 0 && cur_diff < 0) || (pre_diff <= 0 && cur_diff > 0)) {
                result++;
                pre_diff = cur_diff; // 仅满足条件时更新pre_diff(跳过差值为0的情况)
            }
            // 关键:cur_diff=0时,不更新pre_diff,保留上一个非0差值
        }
        return result;

    }
};

53.最大子数组和 重点在于怎么做到局部最优,注意我们是用cur来记录当前最大子序列之和的,所以怎么才是最大。两种方式,一种是通过和记录的result进行比较,将较大值迭代更行。另外一种,即cur小于0时,负数和是越加越小的,那么就没有必要对接下来的子序列进行累加,直接从新的子序列开始累加就行


class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        //暴力解法的n平方复杂度不适用,下面可以改造成n的时间复杂度
        //本题的特殊在于,我们仅仅需要理解什么时候时局部最优,同时记录
        int result=nums[0];
        int cur=0;
        //开始遍历nums
        for(int i=0;i<nums.size();i++){
            cur=cur+nums[i];//cur此时为子序列之和
            //更新result
            if(cur>result) result=cur;
            //重点在这里,怎么使cur移动并达成局部最优了?
            if(cur<0) cur=0;//从这里可以看出,当cur<0时,总和不加就是最小,也就达成了局部最优
        }
        return result;
    }
};

122 买卖股票的最佳时机 这里要扭转一个观点,就是一直持有股票是不是会盈利!!!这里完全是错的,我们是要和下一天做比较,所以,我们每次都是直接取两点进行对比,有盈利我们买入卖出,没有就舍弃,而不是像个预言家一样傻傻地去预测!!思考今天我买了什么时候卖最好,那始终都不是局部最优观点,贪心算法的实质要的就是局部最优,只比较当天最佳选择

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        //遍历数组将正数差累加即可
        int result=0;
        for(int i=0;i<prices.size()-1;i++){
            result+=max(prices[i+1]-prices[i],0);//注意,这里采用贪心算法,局部最优就是当天和前一天的最大利差,亏损舍弃,盈利计入
        }
        return result;
    }
};

55.跳跃游戏 1.理解跳跃的范围,使用贪心算法跟新 2.循环遍历必须摒弃固有思维遍历整个向量,这道题的破题点在于理解i必须在cover内移动

class Solution {
public:
    bool canJump(vector<int>& nums) {
        if(nums.size()==0) return false;
        //本质同股票买入时机区别不大,移动下标更新最大区间即可
        int cover=0;
        for(int i=0;i<=cover;i++){//必须注意,i只能在cover内移动!!!
            cover=max(cover,i+nums[i]);
            if (cover >= nums.size() - 1) return true; // 说明可以覆盖到终点了
        }
        return false;              
    }
};

45。跳跃游戏

此题思路重点在于通过递接两个最大覆盖范围,来减少步数,并不是要求寻找最小步数路径,严格的话确实可以做到在O(n)时间内

class Solution {
public:
    int jump(vector<int>& nums) {
        //通过前后两个覆盖值来记录跳跃步数,这里也是要摆脱常规思想:必须寻找到最佳路径之后记录。但是,在算法当中,这并不需要我们寻找最佳路径
        //剪支
        if(nums.size()==1) return 0;
        int cover=0;//用于记录当前可以移动的范围
        int nextcover=0;//用于记录下一跳可以覆盖的范围
        int result=0;//记录跳跃步数
        for(int i=0;i<nums.size();i++){
            //采用贪心算法获取下一跳的最大覆盖范围
            nextcover=max(i+nums[i],nextcover);
            if(i==cover){//到达当前最大覆盖范围
               result++;//步数加一
               cover=nextcover;//置换最大跳跃范围
               if(nextcover>=nums.size()-1) break;

            }
        }
        return result;
    }
};