代码随想录刷题day28丨122.买卖股票的最佳时机II, 55. 跳跃游戏, 45.跳跃游戏II, 1005.K次取反后最大化的数组和

代码随想录刷题day28丨122.买卖股票的最佳时机II, 55. 跳跃游戏, 45.跳跃游戏II, 1005.K次取反后最大化的数组和

1.题目

1.1买卖股票的最佳时机II

  • 题目链接:122. 买卖股票的最佳时机 II - 力扣(LeetCode)

    在这里插入图片描述

  • 视频讲解:贪心算法也能解决股票问题!LeetCode:122.买卖股票最佳时机II_哔哩哔哩_bilibili

  • 文档讲解:https://programmercarl.com/0122.%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BAII.html

  • 解题思路:贪心

    • 如果想到其实最终利润是可以分解的,那么本题就很容易了!

    • 如何分解呢?

      假如第 0 天买入,第 3 天卖出,那么利润为:prices[3] - prices[0]。

      相当于(prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])。

      此时就是把利润分解为每天为单位的维度,而不是从 0 天到第 3 天整体去考虑!

      那么根据 prices 可以得到每天的利润序列:(prices[i] - prices[i - 1])…(prices[1] - prices[0])。

      如图:

      在这里插入图片描述

  • 代码:

    //时间复杂度:O(n)
    //空间复杂度:O(1)
    class Solution {
        public int maxProfit(int[] prices) {
            int result = 0;
            for(int i = 1;i < prices.length;i++){
                result += Math.max(prices[i] - prices[i - 1],0);
            }
            return result;
        }
    }
    
  • 总结:

    • 至少要第二天才会有利润,所以利润的序列比股票序列少一天!

    • 从图中可以发现,其实我们需要收集每天的正利润就可以,收集正利润的区间,就是股票买卖的区间,而我们只需要关注最终利润,不需要记录区间

    • 那么只收集正利润就是贪心所贪的地方!

    • 局部最优:收集每天的正利润,全局最优:求得最大利润

    • 本题中理解利润拆分是关键点! 不要整块的去看,而是把整体利润拆为每天的利润。

      一旦想到这里了,很自然就会想到贪心了,即:只收集每天的正利润,最后稳稳的就是最大利润了。

1.2跳跃游戏

  • 题目链接:55. 跳跃游戏 - 力扣(LeetCode)

    在这里插入图片描述

  • 视频讲解:贪心算法,怎么跳跃不重要,关键在覆盖范围 | LeetCode:55.跳跃游戏_哔哩哔哩_bilibili

  • 文档讲解:https://programmercarl.com/0055.%E8%B7%B3%E8%B7%83%E6%B8%B8%E6%88%8F.html

  • 解题思路:贪心

    • 其实跳几步无所谓,关键在于可跳的覆盖范围!

    • 不一定非要明确一次究竟跳几步,每次取最大的跳跃步数,这个就是可以跳跃的覆盖范围。

    • 这个范围内,别管是怎么跳的,反正一定可以跳过来。

    • 那么这个问题就转化为跳跃覆盖范围究竟可不可以覆盖到终点!

    • 每次移动取最大跳跃步数(得到最大的覆盖范围),每移动一个单位,就更新最大覆盖范围。

      在这里插入图片描述

  • 代码:

    //时间复杂度: O(n)
    //空间复杂度: O(1)
    class Solution {
        public boolean canJump(int[] nums) {
            int cover = 0;//覆盖下标
            if(nums.length == 1){
                return true;
            }
            for(int i = 0;i <= cover;i++){
                cover = Math.max(i + nums[i],cover);
                if(cover >= nums.length - 1){ // 覆盖范围可以超过
                    return true;
                }
            }
            return false;
        }
    }
    
  • 总结:

    • 贪心算法局部最优解:每次取最大跳跃步数(取最大覆盖范围),整体最优解:最后得到整体最大覆盖范围,看是否能到终点

1.3跳跃游戏II

  • 题目链接:45. 跳跃游戏 II - 力扣(LeetCode)

    在这里插入图片描述

  • 视频讲解:贪心算法,最少跳几步还得看覆盖范围 | LeetCode: 45.跳跃游戏II_哔哩哔哩_bilibili

  • 文档讲解:https://programmercarl.com/0045.%E8%B7%B3%E8%B7%83%E6%B8%B8%E6%88%8FII.html

  • 解题思路:贪心

    • 真正解题的时候,要从覆盖范围出发,不管怎么跳,覆盖范围内一定是可以跳到的,以最小的步数增加覆盖范围,覆盖范围一旦覆盖了终点,得到的就是最少步数!

    • 这里需要统计两个覆盖范围,当前这一步的最大覆盖和下一步最大覆盖

    • 如果移动下标达到了当前这一步的最大覆盖最远距离了,还没有到终点的话,那么就必须再走一步来增加覆盖范围,直到覆盖范围覆盖了终点。

在这里插入图片描述

  • 图中覆盖范围的意义在于,只要红色的区域,最多两步一定可以到!(不用管具体怎么跳,反正一定可以跳到)

  • 代码:

    class Solution {
        public int jump(int[] nums) {
            if(nums.length == 1){
                return 0;
            }
            int cur = 0;//当前覆盖范围
            int next = 0;//下一步覆盖范围
            int result = 0;//记录步数
            for(int i = 0;i < nums.length;i++){
                next = Math.max(i + nums[i],next);
                if(i == cur){
                    if(cur != nums.length){
                        result++;
                        cur = next;
                        if(cur >= nums.length -1){
                            break;
                        }
                    }else{
                        break;
                    }
                }
            }
            return result;
        }
    }
    
  • 总结:

    • 从图中可以看出来,就是移动下标达到了当前覆盖的最远距离下标时,步数就要加一,来增加覆盖距离。最后的步数就是最少步数。
    • 这里还是有个特殊情况需要考虑,当移动下标达到了当前覆盖的最远距离下标时
      • 如果当前覆盖最远距离下标不是是集合终点,步数就加一,还需要继续走。
      • 如果当前覆盖最远距离下标就是是集合终点,步数不用加一,因为不能再往后走了。

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

  • 题目链接:1005. K 次取反后最大化的数组和 - 力扣(LeetCode)

    在这里插入图片描述

  • 视频讲解:贪心算法,这不就是常识?还能叫贪心?LeetCode:1005.K次取反后最大化的数组和_哔哩哔哩_bilibili

  • 文档讲解:https://programmercarl.com/1005.K%E6%AC%A1%E5%8F%96%E5%8F%8D%E5%90%8E%E6%9C%80%E5%A4%A7%E5%8C%96%E7%9A%84%E6%95%B0%E7%BB%84%E5%92%8C.html

  • 解题思路:贪心

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

    //排序数组并贪心地尽可能将负数翻转为正数,再根据剩余的k值调整最小元素的符号,从而最大化数组的总和。
    class Solution {
        public int largestSumAfterKNegations(int[] nums, int k) {
            if(nums.length == 1){
                return nums[0];
            }
            // 排序:先把负数处理了
            Arrays.sort(nums);
            for(int i = 0;i < nums.length && k > 0;i++){ //k > 0很关键
                if(nums[i] < 0){
                    nums[i] = -nums[i];
                    k--;
                }
            }
    
            if(k % 2 == 1){
                Arrays.sort(nums);
                nums[0] = -nums[0];//把最小的正数取反
            }
    
            int sum = 0;
            for(int i = 0;i < nums.length;i++){
                sum += nums[i];
            }
            return sum;
        }
    }
    
  • 总结:

    • 如果没有贪心的思考方式(局部最优,全局最优),很容易陷入贪心简单题凭感觉做,贪心难题直接不会做,其实这样就锻炼不了贪心的思考方式了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值