Leetcode刷题笔记32:贪心2(122.买卖股票-55/45. 跳跃游戏(I,II))

51 阅读3分钟

导语

leetcode刷题笔记记录,本篇博客是贪心部分的第二期,主要记录题目包括:

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

题目描述

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。返回 你能获得的 最大 利润 。

示例 1:

输入: prices = [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
     总利润为 4 + 3 = 7

提示:

  • 1 <= prices.length <= 3 * 10^4
  • 0 <= prices[i] <= 10^4

解法

使用贪心算法的思路,我们需要往最终结果中添加正向收益即可,最终利润是可以分解为每天为单位的维度,而不是整体去考虑,那么每天的利润序列为:

Total=(p[i]p[i1])+(p[i1]p[i2])+.....+(p[1]p[0])Total = (p[i] - p[i - 1]) + (p[i-1] - p[i-2]) + ..... + (p[1] - p[0])

所以,只需要通过计算每天的正向收益,就可以得到最大的总收益。具体代码如下:

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        result = 0
        for i in range(1, len(prices)):
            result += max(0, prices[i]-prices[i-1])

        return result

Leetcode 55 跳跃游戏

题目描述

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。

示例 1:

输入: nums = [2,3,1,1,4]
输出: true
解释: 可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

提示:

  • 1 <= nums.length <= 10^4
  • 0 <= nums[i] <= 10^5

解法

这道题目如果使用模拟将所有情况遍历,则非常复杂。实际上,这里只需要判断是否能够到达,所以转化为跳跃的覆盖范围!w这道题目如果使用模拟将所有情况遍历,则非常复杂。实际上,这里只需要判断是否能够到达,所以转化为跳跃的覆盖范围! 无需明确一次究竟跳几步,每次取最大的跳跃步数,这个就是可以跳跃的覆盖范围。

设计程序,每次移动取最大跳跃步数(得到最大的覆盖范围),每移动一个单位,就更新最大覆盖范围。对应的贪心算法局部最优解:每次取最大跳跃步数(取最大覆盖范围),整体最优解:最后得到整体最大覆盖范围,看是否能到终点

具体代码如下:

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        # 特殊情况处理:只有一个元素的数组可以认为总是可以到达
        if len(nums) == 1:
            return True
        
        # cover表示当前能够跳到的最远位置,i是当前正在检查的位置
        cover, i = 0, 0
        
        # 只要当前的位置i在能到达的最远距离cover内,就继续检查
        while i <= cover:
            # 对于位置i,能够跳到的最远距离是i+nums[i],更新cover为最大的那个值
            cover = max(i + nums[i], cover)
            
            # 如果cover已经大于或等于数组的最后一个位置,说明可以到达最后,返回True
            if cover >= len(nums) - 1:
                return True
            
            # 继续检查下一个位置
            i += 1
        
        # 如果循环结束后仍没有返回True,说明不能到达数组的最后一个位置,返回False
        return False

Leetcode 45 II

题目描述

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

  • 0 <= j <= nums[i] 
  • i + j < n

返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]

示例 1:

输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

提示:

  • 1 <= nums.length <= 104
  • 0 <= nums[i] <= 1000
  • 题目保证可以到达 nums[n-1]

解法

相比于上一题,本题需要求出最少的步数,因此,实际上还是求得最大覆盖范围,只不过这次需要我们统计跳了几次。这里需要统计两个覆盖范围,当前这一步的最大覆盖和下一步最大覆盖。示意图参考代码随想录

image.png

完整代码如下:

class Solution:
    def jump(self, nums: List[int]) -> int:
        # 如果只有一个元素,那么我们已经在目的地了,因此不需要任何跳跃
        if len(nums) == 1:
            return 0

        # cur_distance: 从起点到当前能够到达的最远距离
        # next_distance: 当前元素及之前的元素可以到达的最远距离
        cur_distance, next_distance = 0, 0

        # 跳跃次数
        result = 0

        # 遍历数组元素
        for i in range(len(nums)):
            # 更新next_distance为当前元素可以到达的最远距离与之前的next_distance中的较大值
            next_distance = max(i + nums[i], next_distance)

            # 如果遍历到了当前可达的最远距离
            if i == cur_distance:
                # 如果当前距离不是数组的最后一个位置
                if cur_distance != len(nums) - 1:
                    # 增加跳跃次数
                    result += 1
                    # 更新当前的最远距离为下一步的最远距离
                    cur_distance = next_distance
                    # 如果已经可以到达数组的最后位置,则提前跳出循环
                    if cur_distance >= len(nums) - 1:
                        break

        return result