给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 =
6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;
同时,你不能在买入前卖出股票。
示例 2:输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。提示:
1 <= prices.length <= 105 0 <= prices[i] <= 104
方法一:记录最低价格法(贪心算法)
这种方法的基本思路是在遍历股票价格的过程中,始终记录遇到的最低买入价格,然后计算当前价格卖出所能获得的利润,并不断更新最大利润。
具体步骤
1. 初始化
• 将 min_price 设为一个非常大的值(或数组的第一个价格)。
• 将 max_profit 初始化为 0。
2. 遍历数组
• 对于数组中的每个价格:
• 如果当前价格小于 min_price,则更新 min_price。
• 否则计算当前价格与 min_price 的差值(即利润),并更新 max_profit(如果该利润更高)。
3. 返回结果
• 遍历结束后,max_profit 即为所能获得的最大利润。
def max_profit(prices):
# 初始化最低价格和最大利润
min_price = float('inf')
max_profit = 0
# 遍历每一天的价格
for price in prices:
# 如果当前价格低于之前的最低价格,更新最低价格
if price < min_price:
min_price = price
else:
# 否则计算卖出利润,并更新最大利润
profit = price - min_price
if profit > max_profit:
max_profit = profit
return max_profit
详细解析
• 示例 1: 对于 [7, 1, 5, 3, 6, 4]:
• 第一天价格 7,min_price = 7。
• 第二天价格 1,更新 min_price = 1。
• 第三天价格 5,利润为 5 - 1 = 4,更新 max_profit = 4。
• 第四天价格 3,利润为 3 - 1 = 2,max_profit 保持不变。
• 第五天价格 6,利润为 6 - 1 = 5,更新 max_profit = 5。
• 第六天价格 4,利润为 4 - 1 = 3,max_profit 保持不变。
最终返回 5。
• 示例 2: 对于 [7, 6, 4, 3, 1]:
每天价格均低于前一天,所以没有任何买卖能产生正利润,最终返回 0。
这种方法的时间复杂度是 O(n),空间复杂度是 O(1)。
2.动态规划方法
动态规划的方法将问题转换为在每一天 持有股票 和 不持有股票 两种状态的转移问题。
定义状态
dp[i][0]
:表示第 ( i ) 天结束时 不持有股票 的最大利润。dp[i][1]
:表示第 ( i ) 天结束时 持有股票 的最大利润。
由于题目只允许进行一次交易,状态转移如下:
不持有股票状态
dp[i][0] = max(dp[i-1][0], dp[i-1][1] +prices[i])
其中,dp[i-1][1] + prices[i]
表示 今天卖出股票 的利润。
持有股票状态
dp[i][1] = max(dp[i-1][1], -prices[i])
其中,-prices[i]
表示在第 ( i ) 天 买入股票 的操作(只允许买入一次)。
def max_profit_dp(prices):
if not prices:
return 0
n = len(prices)
# 初始状态:第 0 天不持有股票利润为 0,第 0 天持有股票利润为 -prices[0]
dp0 = 0
dp1 = -prices[0]
for i in range(1, n):
# 更新不持有股票的状态:可以选择继续保持,也可以选择今天卖出
dp0 = max(dp0, dp1 + prices[i])
# 更新持有股票的状态:选择继续持有,或者在今天买入(因为只允许买入一次,所以直接取 -prices[i])
dp1 = max(dp1, -prices[i])
return dp0
3.暴力枚举法
暴力枚举法的思路是遍历所有可能的买入和卖出组合,计算每种组合的利润,并取其中的最大值。
由于需要嵌套遍历所有买卖对,这种方法的时间复杂度为 O(n^2),在数据量较大时效率较低。
def max_profit_brute_force(prices):
n = len(prices)
max_profit = 0
for i in range(n):
for j in range(i+1, n):
profit = prices[j] - prices[i]
if profit > max_profit:
max_profit = profit
return max_profit
总结
• 记录最低价格法(贪心算法):
• 时间复杂度:O(n)
• 空间复杂度:O(1)
• 思路:遍历过程中记录最低买入价,计算并更新最大利润。
• 动态规划法:
• 同样可以在 O(n) 时间内求解。
• 思路:将问题转化为状态转移问题(持有和不持有股票两种状态)。
• 暴力枚举法:
• 时间复杂度:O(n^2)
• 虽然直观易懂,但效率较低,适合数据规模较小的情况。