算法打卡day28

今日任务:

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

2)55. 跳跃游戏

3)45.跳跃游戏II

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

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

给定一个数组,它的第  i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

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

示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例  3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:
1 <= prices.length <= 3 * 10 ^ 4
0 <= prices[i] <= 10 ^ 4

文章讲解:代码随想录 (programmercarl.com)

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

思路:

当我们观察股票价格的变化时,我们可以发现以下规律:

  • 如果股票价格连续上涨,那么我们可以考虑在价格开始上涨的一天买入,在价格达到峰值的一天卖出,这样可以获得最大利润。
  • 如果股票价格连续下跌或持平,那么我们不进行交易,因为这样不会带来利润。

具体步骤

  1. 遍历股票价格数组 prices
  2. 维护一个栈 have,用于记录当前持有的股票价格。
  3. 初始化利润 res 为 0。
  4. 对于每一天的股票价格:
    • 如果当前栈为空,说明还没有持有股票,则将当前股票价格入栈。
    • 否则,如果当前股票价格高于前一天的价格,说明可以在前一天买入,当天卖出,获取利润。
      • 计算利润,并累加到 res 中。
      • 弹出栈顶元素,表示卖出持有的股票。
    • 如果当前股票价格低于前一天的价格,说明前一天的股票价格高于当前价格,因此应该考虑在前一天卖出股票,并更新栈 have
      • 弹出栈顶元素,表示卖出持有的股票。
    • 将当前股票价格入栈,表示持有当前的股票。
  5. 遍历结束后,返回累计的利润 res
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # 初始化一个栈,用于记录当前持有的股票价格
        have = []
        # 初始化利润为 0
        res = 0

        # 遍历股票价格数组
        for i in range(len(prices)):
            # 如果当前栈为空,说明还没有持有股票,则将当前股票价格入栈
            if len(have) == 0:
                have.append(prices[i])
            else:
                # 如果当前股票价格高于前一天的价格,说明可以在前一天买入,当天卖出,获取利润
                if prices[i] > prices[i - 1]:
                    # 计算利润,并累加到 res 中
                    res += prices[i] - have.pop()
                # 如果当前股票价格低于前一天的价格,说明前一天的股票价格高于当前价格,应该在前一天卖出股票,并更新栈 have
                elif prices[i] < prices[i - 1]:
                    # 弹出栈顶元素,表示卖出持有的股票
                    have.pop()
                # 将当前股票价格入栈,表示持有当前的股票
                have.append(prices[i])

        # 返回累计的利润
        return res

思路二

基于刚才的规律,我们可以设计一种贪心的策略:只要当天的价格比前一天的价格高,我们就在前一天买入,当天卖出,这样可以获得最大利润。因为连续的价格上涨可以看作多次买入和卖出的组合,所以这种策略可以得到最大利润。

基于这个思路,我们遍历股票价格数组,比较当前价格与前一天的价格,如果当前价格大于前一天的价格,则可以在前一天买入,第二天卖出,获取利润。最后将所有正的利润累加,即可得到最大利润。

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

55. 跳跃游戏

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

给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。

示例  1:
输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。

示例  2:
输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

文章讲解:代码随想录 (programmercarl.com)

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

思路:

  1. 从数组的倒数第二个位置开始向前遍历数组。
  2. 对于每个位置 i,检查是否可以跳跃到当前位置 cur,即 nums[i] >= cur - i
  3. 如果当前位置可以跳跃到,更新 curi,表示当前位置为新的可到达位置。
  4. 最后检查 cur 是否等于 0,如果等于 0,表示可以到达最后一个位置,返回 True;否则返回 False。
class Solution:
    def canJump2(self, nums: List[int]) -> bool:
        cur = len(nums) - 1  # 初始化当前能够到达的最远位置为数组最后一个位置
        for i in range(len(nums) - 2, -1, -1):  # 从倒数第二个位置向前遍历
            if nums[i] >= cur - i:  # 如果当前位置能够跳到当前所能到达的最远位置
                cur = i  # 更新当前能够到达的最远位置为当前位置
        return cur == 0  # 如果最终能够到达的最远位置是数组的第一个位置,则返回 True,否则返回 False

思路二:

当解决这个问题时,我们希望尽可能地跳跃,以达到最远的位置。我们可以使用贪心算法来解决这个问题。下面是算法的详细步骤:

  1. 我们从数组的第一个位置开始,记录当前能够到达的最远位置为 max_reach,初始值为 0。
  2. 遍历数组,对于当前位置 i
    • 如果当前位置 i 大于 max_reach,说明无法到达最后一个位置,因此返回 False。
    • 否则,更新 max_reach 为当前位置 i 可以达到的最远位置,即 max_reach = max(max_reach, i + nums[i])
  3. 如果遍历结束时,最远位置 max_reach 大于等于最后一个位置(即 max_reach >= len(nums) - 1),则说明可以到达最后一个位置,返回 True,否则返回 False。

这样,我们通过贪心算法来判断是否能够到达最后一个位置。算法的时间复杂度为 O(n),其中 n 是数组的长度。

class Soultion:
    def canJump3(self, nums: List[int]) -> bool:
        # 记录当前能够到达的最远位置
        max_reach = 0

        # 遍历数组
        for i in range(len(nums)):
            # 如果当前位置大于最远位置,说明max_reach无法覆盖位置i,返回 False
            if i > max_reach:
                return False
            # 更新最远位置
            max_reach = max(max_reach, i + nums[i])

        # 如果遍历结束时,最远位置大于等于最后一个位置,返回 True,否则返回 False
        return max_reach >= len(nums) - 1

45.跳跃游戏II

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

给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。从下标为 0 跳到下标为 1 的位置,跳  1  步,然后跳  3  步到达数组的最后一个位置。
说明: 假设你总是可以到达数组的最后一个位置。

文章讲解:代码随想录 (programmercarl.com)

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

思路:

这个问题可以使用贪心算法来解决。我们可以维护两个变量 max_reachsteps,其中 max_reach 表示当前能够到达的最远位置,而 steps 表示跳跃的次数。

首先,我们初始化 max_reachsteps 都为 0,然后我们遍历数组中的每个位置 i,在遍历过程中更新 max_reachmax(max_reach, i + nums[i]),这表示当前位置能够到达的最远位置。当我们遍历到 i 时,如果 i == max_reach,说明我们已经到达了当前能够到达的最远位置,这时我们就需要进行一次跳跃,将 max_reach 更新为 i + nums[i],同时 steps 自增 1。我们遍历完整个数组后,steps 就是我们所需要的最少跳跃次数。

从下标0开始,第一次跳跃最远为2,也就是区间[0,2],此时我们记录end = 2,2不是终点,所以肯定是要跳的,就是看在这个区间中,选择跳到哪,下一次跳跃区间更远。假如第一次跳跃,我们跳到区间中的索引index = 1,那么下一次跳跃的最远距离就是index + nums[index] = 4。假如第一次跳跃,我们跳到区间中的索引index = 2,那么下一次跳跃的最远距离就是index + nums[index] = 3,所以,我们应该选择index=1,此时最远距离为4,我们记录max_reach = 4,遍历的区间终点end=4。在这个遍历第二次跳跃区间的过程中,我们遇到比4大的则更新max_reach,直到遍历完到区间边界end,还没有到终点,则进行二跳。以此类推,直到遍历区间包含终点,则结束。

class Solution:
    def jump(self, nums: List[int]) -> int:
        max_reach = 0  # 当前能够到达的最远位置
        steps = 0  # 跳跃的次数
        end = 0  # 当前步数内能够到达的最远位置
        for i in range(len(nums) - 1):
            max_reach = max(max_reach, i + nums[i])  # 记录当前位置能到达最远位置区间中,下一个最远距离。(即,此次跳跃中,能实现的最大距离)
            if i == end:  # 如果遍历到当前步数内能够到达的最远位置
                end = max_reach  # 更新当前步数内能够到达的最远位置为当前能够到达的最远位置
                steps += 1  # 进行一次跳跃
        return steps

感想:

代码好写,方法不好想,可以先按数学逻辑写出一版基础的,再去优化代码,熟练解题技巧

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值