所用代码 java
买股票的最佳时机 LeetCode 121
题目链接:买股票的最佳时机 LeetCode 121 - 简单
思路
-
dp[i] [0] 、dp[i] [1] :在第i天持有股票所获得的最大利润为dp[i] [0] ;第 i天不持有股票dp[i] [1] 。所以我们在第i天的时候有持有和不持有股票这两种状态,且持有和不持有并不代表着这一天买或者卖,也可能是前面以及买入或者卖出的情况。
-
递推公式:
-
dp[i] [0] = max(dp[i-1] [0], -prices[i])
- dp[i-1] [0]:前一天持有股票的最大利润
- -prices[i] :前一天买入股票,只买一次
-
dp[i-1] [1] = max(dp[i-1] [1], dp[i-1] [0] + prices[i])
- dp[i-1] [1]:前一天不持有股票的最大利润
- dp[i-1] [0] + prices[i]:第i天把股票给卖了,所以今日就要赚prices[i],且前一天买入时可能是赊账买入的,所以得加上前一天持有股票的状态
-
-
初始化:
- dp[0] [0]:- prices[0] 买入相当于是钱少了,所以是负数
- dp[0] [1]:0 不买就是0元
-
遍历顺序:1<=i<prices.length
-
打印dp数组
返回结果:
持有股票的最大利润或不持有股票的最大利润:max(dp[i] [0] , dp[i] [1])
其实不持有股票的最大利润肯定是比持有股票的利润大,可直接输出dp[i] [1]
class Solution {
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length][2];
// 初始化、持有股票和不持有股票两种状态
dp[0][0] = -prices[0]; // 第0天赊账买入
dp[0][1] = 0;
for (int i = 1; i < prices.length; i++) {
// 持有股票 => 前一天持有股票或者今天刚好买入(买一次)
dp[i][0] = Math.max(dp[i-1][0], -prices[i]);
// 不持有股票 => 前一天不持有股票或者今天刚好卖出(卖一次,前一天持有的利润+卖出的利润)
dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] + prices[i]);
}
// 最后一天不持有股票肯定比持有股票利润多
return dp[prices.length-1][1];
}
}
打印结果:
Your input:[7,1,5,3,6,4]
Output:5
Expected:5
stdout:
dp[i][0] = -1 dp[i][1] = 0
dp[i][0] = -1 dp[i][1] = 4
dp[i][0] = -1 dp[i][1] = 4
dp[i][0] = -1 dp[i][1] = 5
dp[i][0] = -1 dp[i][1] = 5
总结
本题是股票买卖动态规划的基础,需好好理解持有股票与不持有股票,当天的利润应是如何获得的。
另外本题的dp数组可以压缩成一维的数组:
-
dp数组:
- dp[0]:持有股票的最大利润
- dp[1]:不持有股票的最大利润
-
递推公式:
- dp[0]:
max(dp[0], -prices[i])
前一天持有或前一天买入 - dp[1]:
max(dp[1], dp[0] + prices[i])
前一天不持有或前一天卖出(前一天卖出=前一天持有+利润)
- dp[0]:
-
初始化:
- dp[0] = -prices[0] 第一天持有(相当于赊账买入)
- dp[1] = 0 第一天不持有
class Solution {
public int maxProfit(int[] prices) {
int[] dp = new int[2];
dp[0] = -prices[0]; // 持有股票
dp[1] = 0; // 不持有股票
for (int i = 1; i < prices.length; i++) {
dp[0] = Math.max(dp[0], -prices[i]);
dp[1] = Math.max(dp[1], dp[0] + prices[i]);
// System.out.println("dp[0] = "+dp[0] + "\tdp[1] = " +dp[1]);
}
return dp[1];
}
}
打印结果:
Your input:[7,1,5,3,6,4]
Output:5
Expected:5
stdout:
dp[0] = -1 dp[1] = 0
dp[0] = -1 dp[1] = 4
dp[0] = -1 dp[1] = 4
dp[0] = -1 dp[1] = 5
dp[0] = -1 dp[1] = 5
贪心: 另外本题还可以直接使用贪心,只需要找到左边最小的数,最右的最大值,即可求得最大利润
class Solution {
public int maxProfit(int[] prices) {
int min = Integer.MAX_VALUE;
int result = 0;
for (int i = 0; i < prices.length; i++) {
// 从左到右找到最小值
min = Math.min(min, prices[i]);
// 每次直接比较最大利润区间
result = Math.max(result, prices[i] - min);
}
return result;
}
}
买卖股票的最佳时机II LeetCode 122
题目链接:买卖股票的最佳时机II LeetCode 122 - 中等
思路
贪心的思路前面就做过了,这里主要考虑使用dp的方法。
和1不一样的地方:可以多次买入和卖出!
- 不持有股票:前一天不持有,或者前一天刚好卖出
- 卖出股票:卖股票可以卖多次,所以除了第一次赊账,后面都可以用前一天赚的钱来买股票,也就是前一天不持有股票的状态。
class Solution {
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length][2];
// 初始化、持有股票和不持有股票两种状态
dp[0][0] = -prices[0]; // 第0天赊账买入
dp[0][1] = 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]);
// System.out.println("dp[i][0] = " + dp[i][0] + "\tdp[i][1] = " + dp[i][1]);
}
// 最后一天不持有股票肯定比持有股票利润多
return dp[prices.length-1][1];
}
}
总结
同样,本题也可以写成一维数组的形式:
class Solution {
public int maxProfit(int[] prices) {
int[] dp = new int[2];
dp[0] = -prices[0]; // 持有买票,买入(赊账)
dp[1] = 0; // 不持有股票
for (int i = 1; i < prices.length; i++) {
// 持有 昨天持有或今日卖出(后面有钱了就不用赊账)
dp[0] = Math.max(dp[0], dp[1]-prices[i]);
// 不持有股票 昨天不持有或今日卖出(今日卖出昨日肯定就持有)
dp[1] = Math.max(dp[1], dp[0]+prices[i]);
}
return dp[1];
}
}
买卖股票重点:
-
今日持有股票的状态:
-
昨日不持有,今日才买入 => 就看前面是否赚钱
- 一次买卖:
-prices[i]
,相当于赊账去买 - 多次买卖:
dp[1]-prices[i]
,第一次赊账买,后面用前面持有是赚的钱去买
- 一次买卖:
-
昨日持有 =>
dp[0]
-
-
今天不持有股票状态:
-
昨日持有,今日卖出 => 都是用昨日持有股票的状态(可能赊账买入),再加上今日卖这张股票赚的钱
- 一次买卖:
dp[0]+prices[i]
- 多次买卖:
dp[0]+prices[i]
- 一次买卖:
-
不持有 => dp[1]
-