题目链接:
题目描述:
给定一个数组 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
首先介绍贪心算法的解法吧
解题思路:
贪心算法的特点是从局部最优推全局最优
这里的局部最优是遍历到第i天则计算第i天的股票价格对利润的影响,以此确定前i天这个局部的最大利润。一直遍历完数组,则这个最大利润就是所求整体的最大利润,具体算法如下
分成2个区间,已知区间(前i天)和未知区间(i天之后),存储区间最小值,则遍历到第i天时最大利润profit = max(profit, prices[i] - min_prices)。profit初始化为0,区间最小值初始化为prices[0]
代码如下:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
min_price = prices[0]
profit = 0
for price in prices[1:]:
if price < min_price: min_price = price#如果第i天的股票价格比当前存的最低价格还小,那么它造成的利润(是个负数)必然小于当前所存的利润,这时直接拿它更新最小值就好了
elif price - min_price > profit: profit = price - min_price#更新最大利润
return profit
下面来看看动态规划的解法:(相比于贪心,个人觉得动态规划的方法更具有普适性)
dp四部曲:
确定dp数组:dp[i][0]表示第i天不持有股票地最大利润,dp[i][1]表示第i天不持有股票地最大利润.
确定递推公式:dp[i][0]=max(dp[i-1][0], dp[i-1][1]+prices[i])表示第i天不持有股票地最大利润为max(前一天不持有股票地最大利润,前一天持有股票地最大利润+今天股票地价格即今天把股票卖了)。。。dp[i][1]=max(dp[i-1][1], dp[i-1][0]-prices[i])表示第i天持有股票地最大利润为max(前一天持有股票地最大利润,前一天没持有股票地最大利润-今天股票的价格即今天把股票买了,这里是因为只能买一次,所以不持有股票地利润只能是0,所以写成 -prices[i])
初始化dp数组:dp[0][0]=0,dp[0][1]=-prices[0],其他都初始化为0
遍历顺序:顺序遍历,因为存在着前后依赖关系。因为dp[i]的情况只与dp[i-1]有关,所以考虑滚动数组,用dp00, dp01, dp10, dp11分别代替dp[i-1][0],dp[i-1][1],dp[i][0],dp[i][1]
代码如下:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp00, dp01, dp10, dp11 = 0, -prices[0], 0, 0
for i in range(1, len(prices)):
dp10 = max(dp00, dp01 + prices[i])
dp11 = max(dp01, -prices[i])#这里是因为只能买一次,所以不持有股票地利润只能是0,如果写成dp00 - prices[i]就是可以买卖对此股票了
dp00 = dp10
dp01 = dp11
return dp10#最后返回的是到最后一天不持有股票的最大利润(因为不持有股票的最大利润必然大于持有的最大利润,都把股票卖了肯定更有钱)
最后的运行结果是贪心算法的速度更快:下图分别是贪心和动态规划的运行情况