股票问题
股票问题指的是通过买卖股票来获得最大的收益,在力扣上有全套的股票问题题目。股票问题连接-https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/
第一类股票问题
给定一个数组,下标是日期,存储的数据是当天的股票价格。现在要求求出怎样买卖股票才能获得最大收益,其中只能进行一次买卖操作,且卖出日期需在买入日期之后。如:[3,1,5,7,4,2,8],最大的收益是在第0天买入,第6天卖出,总共赚取8-1=7收益。有两种方法,一种是双层循环,找到最大差值。另一种方法是通过找遍历的当前的元素的前面的所有元素的最大差值,只需依次循环就行。以下是实现代码:
def maxProfit(prices):
maxd = 0 # maxd存储的是最大差值,初始化为0
if prices:
minv = prices[0] # minv存储的是最小值的元素,初始化为第0天的价格
else:
return 0
for p in prices:
maxd = max(p - minv, maxd) # (当前元素的值减去该元素之前的最小元素的值, 之前循环求得的最大差值)
minv = min(p, minv) # min(当前元素值, 之前循环得到的最小值)
return maxp
第二类股票问题
在上一个问题当中进行了改进,把只能买卖一次变成了可以进行买卖多次,但不能在同一天进行买入和卖出。基本思想就是:假设有以下一组数据[A,B,C,D],且D>C>B>A,那么我们就可以得到:D-A>B-A+D-C。根据这个条件我们就可以得到这样一个结论:最大利润就是每段区域的利润总和,而这每段利润指的就是一段连续升值的区域。这样话我们就可以去以价格下降的当天和前一天最为分界点来得到一段区域。以下是实现代码:
def maxProfit(prices):
if not prices:
return 0
beg = 0 # beg存储的是一段区域的起始位置
num = 0 # end存储的是一段区域的结束位置
l = len(prices)
for i in range(1,l): # 从下标为1开始
if prices[i] > prices[i-1]: # 一段区域是否结束的条件
if i == l-1:
num = num + (prices[i] - prices[beg])
else:
end = i - 1
num = num + (prices[end]-prices[beg])
beg = i
return num
第三类股票问题
第三类股票问题是把可以买卖多次的条件变为可以买卖k次,这里以k=2为例讲解,借鉴的是力扣网上的一个通俗易懂的解法-https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/solution/tong-su-yi-dong-de-dong-tai-gui-hua-jie-fa-by-marc/
这个解法用到的是动态规划,动态规划就是后一步结果是建立在前一步的结果的基础上的,套路就是先创建dp,在写出状态转移方程。这个问题的条件有天数、当天是否持有股票、之前一共进行过几次买卖(包括当天)。例如:dp[3][1][1] = 10代表的含义是第3天手中持有股票且之前买卖过一次,10代表的是这种情况下的利润。
动态转移方程就可列为:
不持有股票的情况:
//之前从没有买卖过的情况,那么利润就是0
dp[i][0][0] = 0
//买卖过一次,可能今天卖的,也可能是之前卖的(之前卖的代表着昨天结束时肯定不持有股票)。
dp[i][0][1] = max(dp[i-1][1][0]+prices[i], dp[i-1][0][1])
//买卖过两次,最后一次买可能是今天卖的, 也可能是之前卖的。
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])
//手中持有股票且之前买卖过一次,可能是今天买的,也可能是之前买的
dp[i][1][1] = max(dp[i-1][0][1] - prices[i], dp[i-1][1][1])
//持有股票且买卖过两次,不存在这种情况,所以设置为负无穷。
dp[i][1][2] = float(’-inf’)
有了状态转移方程,就可以写出代码了:
def maxProfit(prices):
if not prices:
return 0
n = len(prices)
dp = [[[0,0,0],[0,0,0]] for i in range(n)]
# 先对第0天进行初始化
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,n):
dp[i][0][0] = 0
dp[i][0][1] = max(dp[i-1][1][0] + prices[i], dp[i-1][0][1])
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])
dp[i][1][1] = max(dp[i-1][0][1] - prices[i], dp[i-1][1][1])
dp[i][1][2] = float('-inf')
return max(dp[n-1][0][1], dp[n-1][0][2], 0)
需要加上每天的利益的股票问题
这时的利益=卖出价-买入价+期间每天的价格,只需在持有股票的情况下加上当天的利益,以下是以只买卖一次为例子。
def maxProfit(prices):
if not prices:
return 0
n = len(prices)
# 创建dp
dp = [[[0,0],[0,0]] for i in range(n)]
# 第一天不持股且没卖过
dp[0][0][0] = 0
# 第一天不持股,且卖出过一次,因为不存在这种情况,所以为负无穷
dp[0][0][1] = float('-inf')
# 第一天持股,没卖过
dp[0][1][0] = -prices[0]
# 第一天持股,且卖过一次
dp[0][1][1] = float('-inf')
for i in range(1,n):
# 不持股没卖过
dp[i][0][0] = 0
# 不持股,卖过一次.买的这一次可能是之前卖的,也可能是今天卖的
dp[i][0][1] = max(dp[i-1][1][0]+prices[i], dp[i-1][0][1])
# 持股,且没卖过.可能是今天买入的,也可能是之前就存在的(之前存在的就加上今天的利润)
dp[i][1][0] = max(dp[i-1][0][0]-prices[i], dp[i-1][1][0]+prices[i])
# 不存在持股且卖出过一次的情况
dp[i][1][1] = float('-inf')
return dp[n-1][0][1]