買賣股票最佳時機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)