Studying-代码随想录训练营day28| 122.买卖股票的最佳时机II、55. 跳跃游戏、45.跳跃游戏II、1005.K次取反后最大化的数组和

第28天,贪心算法part02,题目难度在增加,要锻炼贪心思维能力(ง •_•)ง,编程语言:C++

目录

122.买卖股票的最佳时机II

55. 跳跃游戏

45.跳跃游戏II

1005.K次取反后最大化的数组和

总结:


122.买卖股票的最佳时机II

文档讲解:代码随想录买卖股票的最佳时机II

视频讲解:手撕买卖股票的最佳时机II

题目:

学习: 本题最关键的在于理解每天价格之间的关系,事实上从第二天起每一天都会存在利润,只不过有的时候利润是负,有的时候利润是正。因此依据贪心思想,我们收集所有的正利润,得到的就会是最大的利润。不拘泥于选择哪一天买进,而是把每天都作为可能买进,可能卖出的选择,从而收割所有的正利润。

代码:

//时间复杂度O(n)
//空间复杂度O(1)
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int result = 0; //统计利润
        //贪心思路,每次只收集正利润
        for(int i = 1; i < prices.size(); i++) {
            //贪心没有套路,只能靠多了解锻炼
            int profit = prices[i] - prices[i - 1];
            if(profit > 0) {
                result += profit;
            }
        }
        return result;
    }
};

总结:贪心没有套路,只能通过多练习题目,了解更多的题型,提高贪心的思维能力。


55. 跳跃游戏

文档讲解:代码随想录跳跃游戏

视频讲解:手撕跳跃游戏

题目:

学习:本题不能拘泥在每一步怎么跳上,因为跳跃的方法有很多种,如果关注点在跳多少,怎么跳上就会使得需要遍历的情况很多,并且数组一旦长起来,耗费的时间也会指数增加。

本题的关键在于理解并找到能够跳跃的最大长度(最大覆盖范围),从下标为0开始,我们每走一步确定当前能够到达的最大长度(局部最优),直到最大长度大于等于数组的长度说明能够到达终点,或者是我们遍历能够达到的最大长度后,还是没有达到终点则说明不能到达终点(整体最优)。用图来表示为:

下标 i 在cover范围内移动,每次我们找到了更大的cover,就进行更新。

代码:

//时间复杂度O(n)
//空间复杂度O(1)
class Solution {
public:
    bool canJump(vector<int>& nums) {
        //贪心策略,找到跳跃的最大覆盖范围
        int cover = 0; //初始化覆盖范围
        for(int i = 0; i <= cover; i++) {
            //更新跳跃的最大覆盖范围
            cover = max(cover, i + nums[i]);
            //说明覆盖到了最后一个节点
            if(maxboard >= nums.size() - 1) return true;
        }
        return false;
    }
};

45.跳跃游戏II

文档讲解:代码随想录跳跃游戏II

视频讲解:手撕跳跃游戏II

题目:

学习: 本题与上一题不同的地方在于,本题需要统计走的步数,不能单纯的看覆盖的范围大小了,同时本题题干也说明了一定能够到达最后的位置。

但本题依旧需要用到每一步的覆盖范围,不同的在于,本题需要用一个变量记录前一步覆盖的范围,而遍历过程中下标i超过了覆盖范围时,就说明要走下一步了,此时前一步的覆盖范围就变成了当前的最大覆盖范围,之后依次类推直到找到覆盖范围到达最后的节点。用图来表示为:

代码:关键在于:1.如果当前覆盖最远距离下标不是是集合终点,步数就加一,还需要继续走。2.如果当前覆盖最远距离下标就是是集合终点,步数不用加一,因为不能再往后走了。

//时间复杂度O(n)
//空间复杂度O(1)
class Solution {
public:
    int jump(vector<int>& nums) {
        if (nums.size() == 1) return 0;
        int result = 0; //记录跳跃的步数
        int precover = 0; //当前覆盖最远距离下标
        int cover = 0; //下一步覆盖最远距离下标
        for(int i = 0; i < nums.size(); i++) {
            cover = max(cover, i + nums[i]);
            if(i == precover) {
                result++;
                precover = cover;
                if (precover >= nums.size() - 1) {
                    return result;
                }
            }
        }
        return result;
    }
};

总结:本题的贪心依旧十分巧妙,理解本题的关键在于: 以最小的步数增加最大的覆盖范围,直到覆盖范围覆盖了终点,这个范围内最少步数一定可以跳到,不用管具体是怎么跳的,不纠结于一步究竟跳一个单位还是两个单位。


1005.K次取反后最大化的数组和

文档讲解:代码随想录K次取反后最大化的数组和

视频讲解:手撕K次取反后最大化的数组和

题目:

学习:显然本题的贪心想法是很容易能够想到的,本题有两个贪心的部分:1.当数组中存在负数,且k大于0时,我们局部最优就是将绝对值最大的负数进行反转,这样才能够最终得到最大的数组之和。2.当数组中不存在负数后,k还大于0,此时我们局部最优的方式应该是将绝对值最小的正整数(包含0)进行反转,并且只对其进行反转,这样的话如果k为偶数,最后还是只有正整数,如果k为负数,那这个负数也会是绝对值最小的那个,最终就能得到数组和最大。

代码:根据上述贪心逻辑,我们可以写出代码如下,主要注意两点:1.要先对数组进行排序;2.要找到绝对值最小的数,包括从负数变为正数后的那些数。

//时间复杂度O(nlogn)排序算法复杂度
//空间复杂度O(1)
class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        //贪心逻辑,先对负数进行取反(先取反绝对值最小的负数)
        //如果k还有剩余,对最小的正数进行取反
        sort(nums.begin(), nums.end());
        int count = INT_MAX; //记录最小的正数,包括负数变成的正数
        int result = 0; //记录最终和
        //取反所有负数
        for (int i = 0; i < nums.size(); i++) {
            if(k > 0 && nums[i] < 0) {
                k--;
                nums[i] *= -1; 
            }
            count = min(count, nums[i]);
            result += nums[i];
        }
        //如果还有奇数次的k,把最小的正数取反,如果是偶数的k就不用了,因为可以对一个数反复取反
        if(k%2 == 1) {
            //把最小的正数取反也就意味着减去两倍的count(减去原先的count,再加上负数的count)
            result -= count*2;
        }
        return result;
    }
};

本题对于排序算法还有可以优化的部分,可以最开始就通过绝对值大小对数组进行排序。最后可以根据以下步骤进行代码编写:

  • 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
  • 第二步:从前向后遍历,遇到负数将其变为正数,同时K--
  • 第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
  • 第四步:求和

代码:

//时间复杂度O(nlogn)
//空间复杂度O(1)
class Solution {
static bool cmp(int a, int b) {
    return abs(a) > abs(b);
}
public:
    int largestSumAfterKNegations(vector<int>& A, int K) {
        sort(A.begin(), A.end(), cmp);       // 第一步
        for (int i = 0; i < A.size(); i++) { // 第二步
            if (A[i] < 0 && K > 0) {
                A[i] *= -1;
                K--;
            }
        }
        if (K % 2 == 1) A[A.size() - 1] *= -1; // 第三步
        int result = 0;
        for (int a : A) result += a;        // 第四步
        return result;
    }
};

总结:

贪心算法,更多的还是考研一个常识性和思维性的东西,需要多加练习,多加锻炼。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值