一、LeetCode121:买卖股票的最佳时机
给定一个数组,它的第i个元素是一支给定股票第i天的价格
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润
注意你不能在买入股票前卖出股票
示例1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格
示例2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为0
解析:
找出给定数组中两个数字之间的最大差值
题解:
public int maxProfit(int[] prices) {
int minPrice = Integer.MAX_VALUE;
int maxProfit = 0;
for (int i = 0; i < prices.length; ++i) {
if (prices[i] < minPrice)
minPrice = prices[i];
maxProfit = Math.max(maxProfit, prices[i] - minPrice);
}
return maxProfit;
}
二、LeetCode122:买卖股票的最佳时机 II
给定一个数组,它的第i个元素是一支给定股票第i天的价格
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
示例1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3
示例2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票
示例3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为0
解析:
遍历整个股票交易日价格列表,策略是所有上涨交易日都买卖(赚到所有利润),所有下降交易日都不买卖(永不亏钱)
题解:
public int maxProfit(int[] prices) {
int maxProfit = 0;
for (int i = 1; i < prices.length; ++i) {
maxProfit += Math.max(prices[i] - prices[i - 1], 0);
}
return maxProfit;
}
三、LeetCode123:买卖股票的最佳时机 III
给定一个数组,它的第i个元素是一支给定的股票在第i天的价格
设计一个算法来计算你所能获取的最大利润。你最多可以完成两笔交易
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
示例1:
输入: [3,3,5,0,0,3,1,4]
输出: 6
解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3
随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3
示例2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票
示例3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这个情况下, 没有交易完成, 所以最大利润为 0
解析:
dp[i][k]
表示前i天最多交易k次的最高收益
状态转移方程:
dp[i][k] = Math.max(dp[i - 1][k], prices[i] - prices[j] + dp[j][k - 1]) (k<i)
题解:
public int maxProfit(int[] prices) {
if (prices.length == 0) return 0;
//dp[i][k] = Math.max(dp[i - 1][k], prices[i] - prices[j] + dp[j][k - 1]) (k<i)
int K = 2;
int[][] dp = new int[prices.length][K + 1];
for (int k = 1; k <= K; ++k) {
for (int i = 1; i < prices.length; ++i) {
Integer max = Integer.MIN_VALUE;
for (int j = 0; j <= i; ++j) {
max = Math.max(max, prices[i] - prices[j] + dp[j][k - 1]);
}
//比较不操作和选择一天买入的哪个值更大
dp[i][k] = Math.max(dp[i - 1][k], max);
}
}
return dp[prices.length - 1][K];
}
优化时间复杂度:
public int maxProfit(int[] prices) {
if (prices.length == 0) return 0;
int K = 2;
int[][] dp = new int[prices.length][K + 1];
for (int k = 1; k <= K; ++k) {
Integer min = prices[0];
for (int i = 1; i < prices.length; ++i) {
min = Math.min(min, prices[i] - dp[i][k - 1]);
dp[i][k] = Math.max(dp[i - 1][k], prices[i] - min);
}
}
return dp[prices.length - 1][K];
}
优化空间复杂度:
public int maxProfit(int[] prices) {
if (prices.length == 0) return 0;
int K = 2;
int[] dp = new int[K + 1];
int[] min = new int[K + 1];
for (int i = 1; i <= K; ++i)
min[i] = prices[0];
for (int i = 1; i < prices.length; ++i) {
for (int k = 1; k <= K; ++k) {
min[k] = Math.min(min[k], prices[i] - dp[k - 1]);
dp[k] = Math.max(dp[k], prices[i] - min[k]);
}
}
return dp[K];
}
四、LeetCode188:买卖股票的最佳时机 IV
给定一个数组,它的第i个元素是一支给定的股票在第i天的价格
设计一个算法来计算你所能获取的最大利润。你最多可以完成k笔交易
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
示例1:
输入: [2,4,1], k = 2
输出: 2
解释: 在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2
示例2:
输入: [3,2,6,5,0,3], k = 2
输出: 7
解释: 在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4
随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3
解析:
按照LeetCode123的思路改造了一下,代码如下:
public int maxProfit(int k, int[] prices) {
if (prices.length == 0) return 0;
int[] dp = new int[k + 1];
int[] min = new int[k + 1];
for (int i = 1; i <= k; ++i)
min[i] = prices[0];
for (int i = 1; i < prices.length; ++i) {
for (int j = 1; j <= k; ++j) {
min[j] = Math.min(min[j], prices[i] - dp[j - 1]);
dp[j] = Math.max(dp[j], prices[i] - min[j]);
}
}
return dp[k];
}
提交时发现超出内存限制了
申请了两个k+1大的数组,当k太大时超出内存限制了
如果数组大小是n,然后一天买,一天卖,最多就是n/2次交易(买入然后卖出算一次交易),如果最多交易k次,k达到了n/2,也就是最多的交易次数,代表可以交易任意次,和LeetCode122要求相同,所以最终的解法根据 k < n / 2 k<n/2 k<n/2和 k > = n / 2 k>=n/2 k>=n/2分开求解
题解:
public int maxProfit(int k, int[] prices) {
if (prices.length == 0) return 0;
if (k > prices.length / 2) return maxProfit(prices);
int[] dp = new int[k + 1];
int[] min = new int[k + 1];
for (int i = 1; i <= k; ++i)
min[i] = prices[0];
for (int i = 1; i < prices.length; ++i) {
for (int j = 1; j <= k; ++j) {
min[j] = Math.min(min[j], prices[i] - dp[j - 1]);
dp[j] = Math.max(dp[j], prices[i] - min[j]);
}
}
return dp[k];
}
public int maxProfit(int[] prices) {
int profit = 0;
for (int i = 1; i < prices.length; i++) {
int sub = prices[i] - prices[i - 1];
if (sub > 0) {
profit += sub;
}
}
return profit;
}
五、LeetCode309:最佳买卖股票时机含冷冻期
给定一个整数数组,其中第i个元素代表了第i天的股票价格
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
卖出股票后,你无法在第二天买入股票(即冷冻期为1天)
示例:
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
解析:
每天可能存在三种状态:
hold:继续持有股票
sold:卖出股票
rest:什么都不做
转换关系如下:
hold: 可由两个情况转换来
前一天hold,当日rest
前一天rest,当日买入股票变为hold
sold:
前一天hold,当日卖出股票
rest:
前一天sold,当日必须rest
前一天rest,当日继续rest
题解:
public int maxProfit(int[] prices) {
//hold:继续持有股票 hold[i]=Math.max(hold[i - 1], rest[i - 1] - prices[i])
//sold:卖出股票 sold[i]=hold[i - 1] + prices[i]
//rest:什么都不做 rest[i]=Math.max(rest[i - 1], sold[i - 1])
int sold = 0, rest = 0, hold = Integer.MIN_VALUE;
for (int price : prices) {
int preSold = sold;
sold = hold + price;
hold = Math.max(hold, rest - price);
rest = Math.max(rest, preSold);
}
return Math.max(sold, rest);
}
六、LeetCode714:买卖股票的最佳时机含手续费
给定一个整数数组prices,其中第i个元素代表了第i天的股票价格;非负整数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
解析:
cash:不持有股票时的最大利润
hold:持有股票时的最大利润
在第i天时,我们需要根据第i-1天的状态来更新cash和hold的值。对于cash,可以保持不变,或者将手上的股票卖出,状态转移方程为
cash = Math.max(cash, hold + prices[i] - fee)
对于hold,可以保持不变,或者买入这一天的股票,状态转移方程为
hold = Math.max(hold, cash - prices[i])
题解:
public int maxProfit(int[] prices, int fee) {
int cash = 0, hold = -prices[0];
for (int i = 1; i < prices.length; ++i) {
cash = Math.max(cash, hold + prices[i] - fee);
hold = Math.max(hold, cash - prices[i]);
}
return cash;
}