第一种,只卖买一次,那就是找出前面最小的买入价格,在最后卖出即可。
121.买卖股票的最佳时机
public int maxProfit(int[] prices) {
if(prices == null || prices.length == 0) return 0;
int min = prices[0];
int max = 0;
for(int i = 1;i < prices.length;i++) {
if(prices[i] - min > max) {
max = prices[i] - min;
}
min = Math.min(min,prices[i]);
}
return max;
}
122.买卖股票的最佳时机②,无限次买卖
贪心,就是当天可以买入股票同时卖出股票,所以每天都可以操作。
public int maxProfit(int[] prices) {
if(prices == null || prices.length == 0) return 0;
int res = 0;
for(int i = 1;i < prices.length;i++) {
if(prices[i] > prices[i - 1]) {
res += prices[i] - prices[i - 1];
}
}
return res;
}
dp也可以做,dp也不难,注意注释
public int maxProfit(int[] prices) {
if(prices == null || prices.length == 0) return 0;
int len = prices.length;
//dp[天数]][0,1代表,0当天不持有,1代表当天持有]]
int[][] dp = new int[len][2];
dp[0][0] = 0;
dp[0][1] = -prices[0];
for(int i = 1;i < len;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[len - 1][0];
}
123.买卖股票的最佳时机③,只能买卖两次
自己可以写出来的dp
public int maxProfit(int[] prices) {
if(prices == null || prices.length == 0) return 0;
int len = prices.length;
//dp[天数][0,当前不持有股票,1代表当前持有股票][0,没有交易过,1交易过1次,2交易过两次],卖出才算一次交易完成
int[][][] dp = new int[len][2][3];
//第一天不持有股票都算是0
dp[0][1][0] = - prices[0];
dp[0][1][1] = - prices[0];
for(int i = 1;i < len;i++) {
dp[i][0][0] = dp[i - 1][0][0];
dp[i][1][0] = Math.max(dp[i - 1][1][0],dp[i - 1][0][0] - prices[i]);
dp[i][0][1] = Math.max(dp[i - 1][0][1],dp[i - 1][1][0] + prices[i]);
dp[i][1][1] = Math.max(dp[i - 1][1][1],dp[i - 1][0][1] - prices[i]);
dp[i][0][2] = Math.max(dp[i - 1][0][2],dp[i - 1][1][1] + prices[i]);
}
return Math.max(dp[len - 1][0][0],Math.max(dp[len - 1][0][1],dp[len - 1][0][2]));
}
二维数组
public int maxProfit(int[] prices) {
if(prices == null || prices.length == 0) return 0;
int len = prices.length;
//dp[天数][0,未交易,1买入一次,2完成交易一次,3买入两次,4完成交易两次]
int[][] dp = new int[len][5];
dp[0][0] = 0;
dp[0][1] = -prices[0];
dp[0][2] = 0;
dp[0][3] = -prices[0];
dp[0][4] = 0;
for(int i = 1;i < len;i++) {
dp[i][0] = 0;
dp[i][1] = Math.max(dp[i - 1][1],dp[i - 1][0] - prices[i]);
dp[i][2] = Math.max(dp[i - 1][2],dp[i - 1][1] + prices[i]);
dp[i][3] = Math.max(dp[i - 1][3],dp[i - 1][2] - prices[i]);
dp[i][4] = Math.max(dp[i - 1][4],dp[i - 1][3] + prices[i]);
}
return Math.max(dp[len - 1][0],Math.max(dp[len - 1][2],dp[len - 1][4]));
}
一维数组
public int maxProfit(int[] prices) {
if(prices == null || prices.length == 0) return 0;
int len = prices.length;
//dp[0,未交易,1买入1次,2完成交易1次,3买入第二次,4完成交易两次]
int[] dp = new int[5];
dp[0] = 0;
dp[1] = - prices[0];
dp[2] = 0;
dp[3] = - prices[0];
dp[4] = 0;
for(int i = 1;i < len;i++) {
dp[1] = Math.max(dp[1],dp[0] - prices[i]);
dp[2] = Math.max(dp[2],dp[1] + prices[i]);
dp[3] = Math.max(dp[3],dp[2] - prices[i]);
dp[4] = Math.max(dp[4],dp[3] + prices[i]);
}
return Math.max(dp[0],Math.max(dp[2],dp[4]));
}
既然可以用一维数组,那就可以用常数级别的变量代替。
使用滚动数组,降低空间复杂度,是我没想到的。
public int maxProfit(int[] prices) {
if(prices == null || prices.length == 0) return 0;
int len = prices.length;
//[notYet,未交易,firstBuy买入1次,firstGet完成交易1次,secondBuy买入第二次,secondGet完成交易两次]
int notYet = 0;
int firstBuy = - prices[0];
int firstGet = 0;
int secondBuy = - prices[0];
int secondGet = 0;
for(int i = 1;i < len;i++) {
firstBuy = Math.max(firstBuy,notYet - prices[i]);
firstGet = Math.max(firstGet,firstBuy + prices[i]);
secondBuy = Math.max(secondBuy,firstGet - prices[i]);
secondGet = Math.max(secondGet,secondBuy + prices[i]);
}
return Math.max(notYet,Math.max(firstGet,secondGet));
}
188.买卖股票的最佳时机④
思路和完成两次是一致的,但是需要注意的是k数值很大的情况,k很大就会直接超出内存限制,因为要创建数组就直接创建一个k长的三维数组,直接超内存了。所以只要k > len / 2就直接用不限次数的贪心来做就能解决这个问题。
public int maxProfit(int k, int[] prices) {
if(prices == null || prices.length == 0) return 0;
int len = prices.length;
if(k * 2 >= len) {
return greedy(prices);
}
//dp[天数][0,不持有,1,持有][交易次数,卖出才算交易完成]
int[][][] dp = new int[len][2][k + 1];
for(int i = 0;i <= k;i++) {
dp[0][1][i] = - prices[0];
}
for(int i = 1;i < len;i++) {
dp[i][0][0] = 0;
dp[i][1][0] = Math.max(dp[i - 1][1][0],dp[i - 1][0][0] - prices[i]);
for(int j = 1;j <= k;j++) {
dp[i][0][j] = Math.max(dp[i - 1][0][j],dp[i - 1][1][j - 1] + prices[i]);
dp[i][1][j] = Math.max(dp[i - 1][1][j],dp[i - 1][0][j] - prices[i]);
}
}
int max = 0;
for(int i = 0;i <= k;i++) {
max = Math.max(max,dp[len - 1][0][i]);
}
return max;
}
public int greedy(int[] prices) {
int res = 0;
for(int i = 1;i < prices.length;i++) {
if(prices[i] > prices[i - 1]) {
res += (prices[i] - prices[i - 1]);
}
}
return res;
}
309.最佳买卖股票时机含冷冻期
状态转移方程不唯一,这个解法只是一种,注意看dp的定义。
代码来源:
李威威
代码链接:
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/solution/dong-tai-gui-hua-by-liweiwei1419-5/
public int maxProfit(int[] prices) {
if(prices == null || prices.length == 0) return 0;
int len = prices.length;
//dp[天数][0,不持有,1,持有非冷冻期,2,不持有,冷冻期]
int[][] dp = new int[len][3];
dp[0][0] = 0;
dp[0][1] = -prices[0];
dp[0][0] = 0;
for(int i = 1;i < len;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[len - 1][0],dp[len - 1][2]);
}