leetcode-----买卖股票最佳时机1、2、3(暴力法,动态规划)

买卖股票最佳时机1:

解法1:(暴力法)

首先,最简单的方法,暴力法:

两个循环,枚举所有买股票和买股票的可能,及其他们的收益。取最大的收益。

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

l=[6,2,8,1,5]
ss = Solution()
ss.maxProfit(l)

时间复杂度:O(N^2),N为价格数组的长度。

空间复杂度:O(1)

 

解法二:动态规划

若含有从列表的左到右扫过来的特征的,可以用动态规划。

如下面代码的 min_prices为例,意思是在遍历列表时,一边遍历一边找最小值,那么当前最小值等min(之前的最小值,当前值),同样 max_profit也是同样原理。因此可以用动态规划实现。因为动态规划的意思就是 当前的状态可以由之前的状态和现在状态决定。

class Solution:
    def maxProfit(self, prices) -> int:
        if prices == []: return 0
        n = len(prices)
        max_profit = 0
        min_prices = prices[0]
        for i in range(n):
            min_prices = min(min_prices,prices[i])
            max_profit = max(max_profit,prices[i]-min_prices)
        return max_profit

l=[6,2,8,1,5]
ss = Solution()
ss.maxProfit(l)

时间复杂度:O(N)

空间复杂度:O(1)

==============================================================================

买卖股票最佳时机2:

思路:由于可以做多次交易,所以相当于求多个阶段的收益,如下图,例如 8大于1,所以就开始结算之前的收益。如果8的后面是9的话,比8大,那就继续往前,直接下一个状态比其小,才开始结算收益。

class Solution:
    def maxProfit(self, prices) -> int:
        if prices == []: return 0
        max_price = prices[0]
        min_prices = prices[0]
        profit = 0
        n = len(prices)
        for i in range(n):
            if max_price > prices[i]:
                profit += (max_price-min_prices)
                max_price = prices[i]
                min_prices = prices[i]
            else:
                max_price = prices[i]
                min_prices = min(min_prices, prices[i])
            if i == n-1:
                profit += (max_price - min_prices)
        return profit
l=[6,2,8,1,5]
ss = Solution()
res = ss.maxProfit(l)
print(res) #10

时间复杂度:O(N) 空间复杂度:O(1)

==============================================================================

买卖股票最佳时机3:

[动态规划思路]:

动态规划的思路就是 “当前的状态由之前的状态决定”。所以只需要设置好初始条件,就可以推出任何时刻的状态。

而且动态规划有一种“暴力法”的味道在里面,因为未经空间优化的动态规划,是维护一个数组dp,此数组包含了所有情况。

回归正题:

我们已知的条件是:

1. 一共是 n 天。

2. 最多只能交易2次。

那么我们可以给状态数组dp开始定义(这步我觉得是比写代码难的),我们把 dp 设成三维的,dp[X][Y][Z]:

第一维X:表示天数,既然是 n 天,那么X的最大长度就是 n-1 (因为数组是从0开始计数的)

第二维Y:表示是否持股,长度为2,要么是0,表示没持股,要么是1,表示持股。

第三维Z:表示已经交易了的次数,长度为3,分别表示没交易过,交易了一次,交易了两次。

所以数组dp 记录了每种情况下的收益。

我们的步骤是:

1.先初始化初始条件,即第0天时,各个情况的收益:

        #第一天休息
        dp[0][0][0]=0
        #第一天买入
        dp[0][1][0]=-prices[0]
        # 第一天不可能已经有卖出
        dp[0][0][1] = float('-inf')  # -inf在python中表示负无穷大
        dp[0][0][2] = float('-inf')
        #第一天不可能已经卖出
        dp[0][1][1]=float('-inf')
        dp[0][1][2]=float('-inf')

2. 根据初始条件(第一天的情况),逐步开始推导第二天、第三天.....的情况:

        for i in range(1,length):
            #未持股,未卖出过,说明从未进行过买卖
            dp[i][0][0]=0
            #未持股,卖出过1次,可能是今天卖的,可能是之前卖的
            dp[i][0][1]=max(dp[i-1][1][0]+prices[i],dp[i-1][0][1])
            #未持股,卖出过2次,可能是今天卖的,可能是之前卖的
            dp[i][0][2]=max(dp[i-1][1][1]+prices[i],dp[i-1][0][2])
            #持股,未卖出过,可能是今天买的,可能是之前买的
            dp[i][1][0]=max(dp[i-1][0][0]-prices[i],dp[i-1][1][0])
            #持股,卖出过1次,可能是今天买的,可能是之前买的
            dp[i][1][1]=max(dp[i-1][0][1]-prices[i],dp[i-1][1][1])
            #持股,卖出过2次,不可能
            dp[i][1][2]=float('-inf')

3. 最后从最后一天的 交易1次的收益 和 交易2次的收益中取最大的那个即可,当然收益必须大于0,否则最高收益是0

完整代码:

class Solution:
    def maxProfit(self, prices):
        if prices==[]:
            return 0
        length=len(prices)
        #结束时的最高利润=[天数][是否持有股票][卖出次数]
        dp=[ [[0,0,0],[0,0,0] ] for i in range(0,length) ]
        #第一天休息
        dp[0][0][0]=0
        #第一天买入
        dp[0][1][0]=-prices[0]
        # 第一天不可能已经有卖出
        dp[0][0][1] = float('-inf')
        dp[0][0][2] = float('-inf')
        #第一天不可能已经卖出
        dp[0][1][1]=float('-inf')
        dp[0][1][2]=float('-inf')
        for i in range(1,length):
            #未持股,未卖出过,说明从未进行过买卖
            dp[i][0][0]=0
            #未持股,卖出过1次,可能是今天卖的,可能是之前卖的
            dp[i][0][1]=max(dp[i-1][1][0]+prices[i],dp[i-1][0][1])
            #未持股,卖出过2次,可能是今天卖的,可能是之前卖的
            dp[i][0][2]=max(dp[i-1][1][1]+prices[i],dp[i-1][0][2])
            #持股,未卖出过,可能是今天买的,可能是之前买的
            dp[i][1][0]=max(dp[i-1][0][0]-prices[i],dp[i-1][1][0])
            #持股,卖出过1次,可能是今天买的,可能是之前买的
            dp[i][1][1]=max(dp[i-1][0][1]-prices[i],dp[i-1][1][1])
            #持股,卖出过2次,不可能
            dp[i][1][2]=float('-inf')
        return max(dp[length-1][0][1],dp[length-1][0][2],0)

s = Solution()
res = s.maxProfit([8,7,4,7,5,4,8,16,2])
print(res) #15

为了理解,下面给出数组dp的内容,还是以上述代码的例子为例:

里面记录着各种情况的收益,。-inf是无穷的意思,表示不可能事件。

[
    [[0, -inf, -inf], [-8, -inf, -inf]],   #第一天
    [[0, -1, -inf], [-7, -inf, -inf]],     #第二天
    [[0, -1, -inf], [-4, -5, -inf]],     #第三天
    [[0, 3, 2], [-4, -5, -inf]], 
    [[0, 3, 2], [-4, -2, -inf]],
    [[0, 3, 2], [-4, -1, -inf]], 
    [[0, 4, 7], [-4, -1, -inf]], 
    [[0, 12, 15], [-4, -1, -inf]],
    [[0, 12, 15], [-2, 10, -inf]]        #最后一天
]

【简化版】:

上面的版本是动态规划的基础版本,因为数组dp没优化。但是我们仔细看可以看出,数组dp基本上只用了一次,所以可以用几个遍历表示:

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices) <= 1: return 0 
        #n_01表示第n天,0表示没持股,1表示已经交易了1次
        n_01, n_02, n_10, n_11 = float("-inf"), float("-inf"), -prices[0], float("-inf")
        for i in range(1, len(prices)):
            c_01 = max(n_01, n_10+prices[i])
            c_02 = max(n_02, n_11+prices[i])
            c_10 = max(n_10, 0 - prices[i])
            c_11 = max(n_11, n_01-prices[i])

            n_01, n_02, n_10, n_11 = c_01, c_02, c_10, c_11
        return max(n_01, n_02, 0)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值