股票问题
121题
给定一个数组prices,其中prices[i]是一支给定股票第i天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。但是,你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例
- 输入:
prices = [7,1,5,3,6,4] - 输出:
5 - 解释:在第2天(股票价格=1)的时候买入,在第5天(股票价格=6)的时候卖出,最大利润=6-1=5。注意利润不能是7-1=6,因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
解题思路
贪心法:设置一个买入的价格,每次遇到比买入价格大,就换掉。并且每次都要更新最大收益
public int maxProfit(int[] prices) {
int max=0;
int buy=prices[0];
for (int i = 1; i < prices.length; i++) {
max=Math.max(max,prices[i]-buy);
if (prices[i]<buy){
buy=prices[i];
}
}
return max;
}
其他的股票问题均可以使用动态规划解答
动态规划主要是解决两个问题:枚举状态和选择
股票问题的状态:
天数:i
允许交易的最大次数:k
用户账户当前状态(持有或者未持有股票):0,1
dp[i][k][0 or 1] //前面说了三个维度,自然dp数组也是三维的。dp表示利润
for(int i = 0;i < n;i++)
for(int j = 0;j < k;j++)
dp[i][j][0] 取优
dp[i][j][1] 取优//账户状态这个维度只有两种可能,就直接计算就好啦。
122题(k无限制)
给定一个数组prices,其中prices[i]表示某支股票第i天的价格。在每一天,你可以决定是否购买和/或出售股票。你在任何时候最多只能持有一股股票。你也可以先购买,然后在同一天出售。返回你能获得的最大利润。
示例
- 输入:
prices = [7,1,5,3,6,4]
输出:7
解释:在第2天(股票价格=1)的时候买入,在第3天(股票价格=5)的时候卖出,这笔交易所能获得利润=5-1=4。随后,在第4天(股票价格=3)的时候买入,在第5天(股票价格=6)的时候卖出,这笔交易所能获得利润=6-3=3。总利润为4+3=7。 - 输入:
prices = [1,2,3,4,5]
输出:4
解释:在第1天(股票价格=1)的时候买入,在第5天(股票价格=5)的时候卖出,这笔交易所能获得利润=5-1=4。总利润为4。 - 输入:
prices = [7,6,4,3,1]
输出:0
解释:在这种情况下,交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为0。
解题思路
在这个问题中,我们使用动态规划来寻找最优解。我们定义一个二维数组dp,其中dp[i][0]表示第i天交易完后手里没有股票的最大利润,而dp[i][1]表示第i天交易完后手里持有一支股票的最大利润。
根据题目描述,我们可以得到状态转移方程如下:
-
如果第
i天没有股票(dp[i][0]),那么有两种情况:- 第
i-1天也没有股票,并且今天没有进行任何操作(即不买入也不卖出),所以dp[i][0] = dp[i-1][0]。 - 第
i-1天持有股票,但今天卖出了股票,所以dp[i][0] = dp[i-1][1] + prices[i](其中prices[i]是第i天的股票价格)。
取这两种情况中的较大值,即:
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i])。 - 第
-
如果第
i天持有股票(dp[i][1]),那么也有两种情况:- 第
i-1天也持有股票,并且今天没有进行任何操作(即不卖出也不买入),所以dp[i][1] = dp[i-1][1]。 - 第
i-1天没有股票,但今天买入了股票,所以dp[i][1] = dp[i-1][0] - prices[i]。
取这两种情况中的较大值,即:
dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i])。 - 第
注意,初始化时,dp[0][0]应该是0(因为没有股票且没有进行任何交易),而dp[0][1]应该是-prices[0](因为我们在第一天买入了一支股票)。
最后,答案就是dp[n-1][0],其中n是股票价格的长度,表示最后一天交易完后手里没有股票的最大利润。
class Solution {
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length][2];//0为未持有,1为持有
dp[0][0] = 0;
dp[0][1] = 0 - prices[0];
for(int i = 1;i < prices.length;i++){
dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1] + prices[i]);
dp[i][1] = Math.max(dp[i - 1][1],dp[i - 1][0] - prices[i]);
}
return dp[prices.length - 1][0];
}
}
714题(有费用)
买卖股票的最佳时机含手续费
给定一个整数数组 prices,其中第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
此外,你还需要支付交易费用,每次交易(买入或卖出)都需要支付一个固定的费用 fee。
返回你可以从这笔交易中获取的最大利润。
示例 1:
输入: prices = [1, 3, 2, 8, 4, 9], fee = 2
输出: 8
解释: 能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8
解题思路
如果第i天不持有股票(dp[i][0]):
第i-1天也不持有股票,今天没有进行任何操作:dp[i][0] = dp[i-1][0]
第i-1天持有股票,今天卖出了股票,需要支付手续费:dp[i][0] = dp[i-1][1] + prices[i] - fee
取这两者中的较大值:
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee);
如果第i天持有股票(dp[i][1]):
第i-1天就持有股票,今天没有进行任何操作:dp[i][1] = dp[i-1][1]
第i-1天不持有股票,但今天买入了股票(注意:这里不直接考虑手续费,因为手续费是在卖出时支付的):dp[i][1] = dp[i-1][0] - prices[i]
取这两者中的较大值:
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
class Solution {
public int maxProfit(int[] prices, int fee) {
int[][] dp = new int[prices.length][2];//0为未持有,1为持有
dp[0][0] = 0;
dp[0][1] = 0 - prices[0];
for(int i = 1;i < prices.length;i++){
dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1] + prices[i] - fee);
dp[i][1] = Math.max(dp[i - 1][1],dp[i - 1][0] - prices[i]);
}
return dp[prices.length - 1][0];
}
}
309
题目描述
给定一个整数数组prices,其中第i个元素是一支给定股票第i天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
- 交易限制:你不能同时参与多笔交易(即你必须在再次购买前出售掉之前的股票)。
- 冷冻期:卖出股票后,你无法在第二天买入股票(即冷冻期为1天)。
示例
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]。
解题思路
通过定义一个二维数组 dp 来存储不同状态下的最大利润。
变量定义
int n = prices.length;:获取输入股票价格数组的长度。int[][] dp = new int[n][3];:定义一个二维数组dp,其中dp[i][0]表示第i天不持有股票的最大利润,dp[i][1]表示第i天持有股票的最大利润,dp[i][2]表示第i天处于冷冻期的最大利润。
初始化
dp[0][0] = 0;:第一天不持有股票时利润为 0,因为还没有进行任何买卖操作。dp[0][1] = -prices[0];:第一天持有股票,意味着花费了prices[0]的成本,所以利润为-prices[0]。dp[0][2] = 0;:第一天不可能处于冷冻期,所以利润为 0。
状态转移方程
-
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);:- 第
i天不持有股票有两种情况:- 第
i - 1天就不持有股票,利润为dp[i - 1][0]。 - 第
i - 1天持有股票,第i天卖出,利润为dp[i - 1][1] + prices[i](卖出价格为prices[i])。
- 第
- 取这两种情况的较大值作为第
i天不持有股票的最大利润。
- 第
-
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][2] - prices[i]);:- 第
i天持有股票有两种情况:- 第
i - 1天就持有股票,利润为dp[i - 1][1]。 - 第
i - 1天处于冷冻期,第i天买入股票,利润为dp[i - 1][2] - prices[i](买入价格为prices[i])。
- 第
- 取这两种情况的较大值作为第
i天持有股票的最大利润。
- 第
-
dp[i][2] = dp[i - 1][0];:- 第
i天处于冷冻期,说明第i - 1天卖出了股票,所以第i天的冷冻期利润等于第i - 1天不持有股票的利润,即dp[i - 1][0]。
- 第
最终结果
返回 Math.max(dp[n - 1][0], dp[n - 1][2]),因为最后一天的最大利润可能是不持有股票(dp[n - 1][0])或者处于冷冻期(dp[n - 1][2])的情况,取两者中的较大值作为最终的最大利润。
class Solution {
public int maxProfit(int[] prices) {
if (prices == null || prices.length <= 1) {
return 0;
}
int n = prices.length;
// dp[i][0] 表示第 i 天不持有股票的最大利润
// dp[i][1] 表示第 i 天持有股票的最大利润
// dp[i][2] 表示第 i 天处于冷冻期的最大利润
int[][] dp = new int[n][3];
dp[0][0] = 0;
dp[0][1] = -prices[0];
dp[0][2] = 0;
for (int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][2] - prices[i]);
dp[i][2] = dp[i - 1][0];
}
return Math.max(dp[n - 1][0], dp[n - 1][2]);
}
}
LeetCode 188(最多买卖K次)
题目描述:
给定一个数组,它的第i个元素是一支给定的股票在第i天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成k笔交易。注意,你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例:
-
输入:prices = [2, 4, 1], k = 2
输出:2
解释:在第1天(股票价格=2)的时候买入,在第2天(股票价格=4)的时候卖出,这笔交易所能获得利润=4-2=2。 -
输入:prices = [3, 2, 6, 5, 0, 3], k = 2
输出:7
解释:在第2天(股票价格=2)的时候买入,在第3天(股票价格=6)的时候卖出,这笔交易所能获得利润=6-2=4。随后,在第5天(股票价格=0)的时候买入,在第6天(股票价格=3)的时候卖出,这笔交易所能获得利润=3-0=3。
解题思路:
枚举框架解释:
需要对三个维度进行枚举:
- “天”:即遍历每一天,观察在不同天数下的股票状态和利润情况。
- “最大交易次数”:考虑不同的交易次数限制对最终利润的影响。
- “账户状态”:分为持有股票和未持有股票两种状态。
状态转移方程解释:
-
当天未持有股票的情况:
- 第一种情况是昨天就未持有,今天也不买,此时利润与昨天未持有股票时的利润相同,即
dp[i - 1][j][0]。 - 第二种情况是昨天持有,今天卖出。卖出操作不会增加交易次数,因为一次完整的买入和卖出算作一次交易,所以是在当前交易次数维度进行转移,利润为昨天持有股票时的利润加上今天卖出的价格,即
dp[i - 1][j][1] + prices[i]。 - 最终当天未持有股票的最大利润是这两种情况中的较大值,即
dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i])。
- 第一种情况是昨天就未持有,今天也不买,此时利润与昨天未持有股票时的利润相同,即
-
当天持有股票的情况:
- 第一种情况是昨天持有,今天继续持有,利润与昨天持有股票时的利润相同,即
dp[i - 1][j][1]。 - 第二种情况是昨天未持有,今天买入。注意买入操作会引起交易次数的变化,因为买入算作一次交易的开始,所以要从昨天未持有且交易次数少一次的状态转移过来,利润为昨天未持有且交易次数为
j - 1时的利润减去今天买入的价格,即dp[i - 1][j - 1][0] - prices[i]。 - 最终当天持有股票的最大利润是这两种情况中的较大值,即
dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i])。
- 第一种情况是昨天持有,今天继续持有,利润与昨天持有股票时的利润相同,即
class Solution {
public int maxProfit(int k, int[] prices) {
if (prices == null || prices.length < 2) {
return 0;
}
// dp[i][j][0]表示第i天、进行了j次交易且当前未持有股票时的最大利润
// dp[i][j][1]表示第i天、进行了j次交易且当前持有股票时的最大利润
int[][][] dp = new int[prices.length][k + 1][2];
// 初始化第一天的情况
for (int i = 0; i <= k; i++) {
// 第一天无论进行多少次交易,当持有股票时,利润为0 - prices[0],即花费prices[0]的价格买入股票
dp[0][i][1] = 0 - prices[0];
}
// 从第二天开始计算每一天的状态
for (int i = 1; i < prices.length; i++) {
for (int j = 1; j <= k; j++) {
// 第i天未持有股票的最大利润
// 情况一:前一天就未持有股票,即dp[i - 1][j][0]
// 情况二:前一天持有股票,今天卖出,利润为dp[i - 1][j][1] + prices[i]
dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
// 第i天持有股票的最大利润
// 情况一:前一天就持有股票,即dp[i - 1][j][1]
// 情况二:前一天未持有股票,今天买入,并且由于买入操作会使交易次数减少一次,
// 所以是从dp[i - 1][j - 1][0]的状态转移过来,利润为dp[i - 1][j - 1][0] - prices[i]
dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
}
}
// 返回最后一天,进行了k次交易且未持有股票时的最大利润
return dp[prices.length - 1][k][0];
}
}
LeetCode 188(最多买卖2次)
解题思路:
按照188题,只需要把k改成2,就能解决。

浙公网安备 33010602011771号