122.买卖股票的最佳时机 II
给定一个数组,它的第 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
这个问题的解决思路是使用贪心算法。贪心算法在每一步都做出在当前看来最好的选择,希望通过这种局部最优解来获得全局最优解。
在这个问题中,我们可以在每一天都检查股票价格是否上涨。如果明天的价格比今天的高,那么我们就在今天买入,明天卖出。这样可以保证我们能获取到所有的上涨交易日的利润。
具体来说,我们可以遍历价格数组,对于数组中的每一个价格,都和前一天的价格进行比较。如果今天的价格比昨天的高,那么我们就将这个差值加到总利润中。这个差值实际上就是如果我们昨天买入,今天卖出能获得的利润。
通过这种方法,我们可以确保捕捉到每一次价格上涨,从而获得最大的总利润。
from typing import List
class Solution:
def maxProfit(self, prices: List[int]) -> int:
# 初始化利润为0
profit = 0
# 遍历价格数组中的每一天
for i in range(1, len(prices)):
# 如果今天的价格比昨天的高
if prices[i] > prices[i - 1]:
# 那么我们就将这个差值加到总利润中
profit += prices[i] - prices[i - 1]
# 返回总利润
return profit
55. 跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例 1:
- 输入: [2,3,1,1,4]
- 输出: true
- 解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例 2:
- 输入: [3,2,1,0,4]
- 输出: false
- 解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。
这个问题的解决思路是使用贪心算法。贪心算法在每一步都做出在当前看来最好的选择,希望通过这种局部最优解来获得全局最优解。
在这个问题中,我们可以在每一步都尽可能地跳得更远。具体来说,我们可以遍历整个数组,并在所有可到达的位置中更新最远可到达的位置。如果最远可到达的位置大于等于数组的最后一个位置,那么就意味着我们可以到达数组的最后一个位置。
例如,对于输入 [2,3,1,1,4]
,我们首先在位置 0
,此时最远可到达的位置为 2
。然后我们来到位置 1
,发现如果从这里跳跃,我们可以到达更远的位置 4
,所以我们更新最远可到达的位置为 4
。以此类推,我们最终可以到达数组的最后一个位置。
from typing import List
class Solution:
def canJump(self, nums: List[int]) -> bool:
# 初始化当前能到达最远的位置
max_i = 0
# 遍历数组中的每一个位置
for i, jump in enumerate(nums):
# 如果当前位置能到达,并且当前位置+跳数>最远能到达的位置
if max_i>=i and i+jump>max_i:
# 更新最远能到达的位置
max_i = i+jump
# 如果最远能到达的位置>=数组的最后一个位置,那么就返回 True
return max_i>=i
45.跳跃游戏 II
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
- 输入: [2,3,1,1,4]
- 输出: 2
- 解释: 跳到最后一个位置的最小跳跃数是 2。从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
说明: 假设你总是可以到达数组的最后一个位置。
解决此问题的核心思路是使用贪心策略,始终试图跳到可以到达的最远位置。我们可以将其分为以下步骤:
解题思路:
-
初始化变量:
maxPos
:在遍历过程中可以到达的最远位置。end
:当前的跳跃边界。当你到达这个边界时,你必须进行另一个跳跃。jumps
:到目前为止进行的跳跃次数。
-
遍历数组:开始从第一个元素遍历到最后一个元素之前的元素。
-
在每一步,更新
maxPos
为当前位置和当前位置的值之和(即i + nums[i]
)与maxPos
中的较大值。这样做是为了找到从当前位置出发可以到达的最远位置。 -
检查是否到达了当前的跳跃边界,即是否
i == end
。- 如果到达了,那意味着需要进行另一个跳跃。因此,增加
jumps
计数。 - 更新
end
为maxPos
,因为这将是下一个跳跃的最远边界。
- 如果到达了,那意味着需要进行另一个跳跃。因此,增加
-
-
返回结果:遍历结束后,返回
jumps
作为结果。
这种策略是贪心的,因为我们总是试图在当前跳跃中到达最远位置,而不是仅仅考虑下一步。我们这样做是因为到达更远的位置可能会为后续的跳跃提供更好的选择,从而减少总的跳跃次数。
回到示例 [2,3,1,1,4]
:
- 在下标0,你可以跳到1或2。但是,如果你跳到下标1(因为从那里你可以跳更远,到下标4),而不是下标2,你最终会用更少的跳跃次数到达末尾。这就是为什么贪心策略在这里有效的原因。
我们通过给定的示例 [2,3,1,1,4]
来详细解释这个算法的工作原理。
数组为: [2,3,1,1,4]
我们的目标是以最少的跳跃次数从数组的开始位置(下标0)跳到最后位置(下标4)。
步骤1:初始化
maxPos = 0
:初始化时,我们还没有开始移动,所以可达的最远位置是0。end = 0
:我们的当前跳跃边界也是0。jumps = 0
:还没有开始跳跃,所以跳跃次数是0。
步骤2:开始遍历数组
-
当
i = 0
,nums[i] = 2
:maxPos = max(0, 0+2) = 2
:这意味着从当前位置最远可以跳到下标2。- 由于
i
还没有到达end
,所以我们继续。
-
当
i = 1
,nums[i] = 3
:maxPos = max(2, 1+3) = 4
:从这里我们可以跳到数组的最后位置!- 但是,我们仍然没有达到
end
,继续前进。
-
当
i = 2
,nums[i] = 1
:maxPos = max(4, 2+1) = 4
:这没有改变maxPos
。- 现在,我们到达了
end
(因为i
等于end
)。这意味着我们必须进行一个跳跃。最佳的跳跃方式是跳到可以带我们到maxPos
的位置,即下标1处。因此,我们增加跳跃次数jumps = 1
,并更新end
为maxPos
,即end = 4
。
-
当
i = 3
,nums[i] = 1
:maxPos = max(4, 3+1) = 4
:这还是没有改变maxPos
。- 我们还没有到达
end
,所以继续。
步骤3:结束
当我们到达下标4时,遍历结束。此时,我们已经达到了end
,所以再次增加跳跃次数 jumps = 2
。并且,这次我们已经到达了数组的末尾。
因此,总的跳跃次数是2,这就是我们的答案。
简而言之,我们遍历数组,每次都尝试找到下一个跳跃可以到达的最远位置。当我们到达当前的跳跃边界时,我们知道需要进行另一个跳跃,并更新跳跃边界为可以达到的最远位置。
from typing import List
class Solution:
def jump(self, nums: List[int]) -> int:
# 初始化
n = len(nums)
# 如果数组长度为1,那么不需要跳跃,直接返回0
if n == 1:
return 0
# maxPos表示在遍历过程中可以到达的最远位置
maxPos = 0
# end表示当前跳跃的最远边界
end = 0
# jumps表示已经跳跃的次数
jumps = 0
# 遍历数组
for i in range(n):
# 更新可以到达的最远位置
maxPos = max(maxPos, i + nums[i])
# 如果到达当前跳跃的边界
if i == end:
# 进行跳跃,跳跃次数增加
jumps += 1
# 更新跳跃边界为当前可以到达的最远位置
end = maxPos
# 如果已经可以跳到数组的末尾,那么直接返回
if end >= n - 1:
return jumps
return jumps