一次交易,找到买点最低,卖点最高即可。但限制条件是:卖点必须在买点之后。
所以很适合栈来操作,遇到比栈顶小的元素,就入栈,否则,将当前元素减去栈顶元素,然后不断更新最大值即可。
class Solution {
public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0){
return 0;
}
Deque<Integer> stack = new ArrayDeque<>();
stack.push(prices[0]);
int max = 0;
for (int i=1;i<prices.length;i++){
if (prices[i] <= stack.peek()){
stack.push(prices[i]);
}else{
max = Math.max(max, prices[i] - stack.peek());
}
}
return max;
}
}
优化建议:当然也可以用一个指针来记录栈顶元素,即遍历过程中遇到的更小的值,来优化空间。
这个没有交易次数的限制,只要卖点比买点高,就是赚了。
class Solution {
public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0){
return 0;
}
int max = 0;
for (int i=1;i<prices.length;i++){
if (prices[i] > prices[i-1]){
max += (prices[i] - prices[i-1]);
}
}
return max;
}
}
这个限制条件在于最多只能完成两笔交易,也就是说可以不交易,可以1次交易,可以2次交易。
其实可以理解为[3,3,5,0,0,3,1,4],从中间加个隔板,分成[3,3,5|||0,0,3,1,4],|||左边是第一次交易,右边是第二次交易,两次交易的最大利润即为所求。极端情况下,会退化成0次交易(最左边)和1次交易(最右边),我们需要做的就是尝试一下隔板具体在哪个位置的时候,能获得交易的最大值。
1.隔板左右都是进行一场交易,求最大值,就和121题一样了,外面需要加个循环,不断更新隔板的位置,然后不断更新最大值即可。
2.还有一种解法,就是可以从左往后扫描一遍,存下每个位置对应的一次交易获得的最大利润,相当于是隔板左边的交易最大值,隔板右边交易的最大值,可以从右边往左边遍历,然后求得每个位置对应的一次交易获得的最大值,然后将两个数组中对应位置元素夹起来就是该位置放置隔板,两次交易获得的最大值,求和后数组中的最大值即为最大利润。
class Solution {
public int maxProfit(int[] prices) {
if(prices == null || prices.length == 0){
return 0;
}
int n = prices.length;
int[] arr = new int[n];
int idx = 0;
for(int i=1;i<n;i++){
if(prices[i] <= prices[idx]){
idx = i;
}
arr[i] = Math.max(arr[i-1], prices[i] - prices[idx]);
}
int max = arr[n-1];
idx = n - 1;
int m = 0;
for(int i=n-2;i>=0;i--){
if(prices[i] >= prices[idx]){
idx = i;
}
m = Math.max(m, prices[idx] - prices[i]);
arr[i] += m;
max = Math.max(max, arr[i]);
}
return max;
}
}
这道题可以多次交易,但多了个条件,冷冻期限制。看了一些题解,许多都是将状态划分为3个,来处理的,不过我感觉还是两个好理解一点。其实无论是不是冷冻期,对于某一天来说,只有两个状态,持有股票和不持有股票,冷冻期的时候,肯定是前一天卖出股票了,而后一天不能买入,肯定也是不持有状态。
所以可以定义
hold[i]:第i天持有股票的最大收益;
unhold[i]:第i天未持有股票的最大收益。
很容易想到转移方程:
hold[i] = Math.max(hold[i-1], unhold[i-1] - prices[i]);------1
unhold[i] = Math.max(unhold[i-1], hold[i-1] + prices[i]);------2
但1式其实有点问题,因为: unhold[i-1] = Math.max(unhold[i-2], hold[i-2] + prices[i-1]); 如果unhold[i-1]取值是hold[i-2] + prices[i-1],说明第i-2天持有股票,第i-1天给卖出去了,那么第i天就是冷冻期,就不能参与买入交易了。所以unhold[i-1]必须取值为unhold[i-2]才能在第i天买入。
于是状态方程就变成了:
hold[i] = Math.max(hold[i-1], unhold[i-2] - prices[i]);
unhold[i] = Math.max(unhold[i-1], hold[i-1] + prices[i]);
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
if (n < 2){
return 0;
}
int[] hold = new int[n];
int[] unhold = new int[n];
hold[0] = -prices[0];
hold[1] = Math.max(-prices[0], -prices[1]);
unhold[1] = Math.max(0, prices[1] - prices[0]);
for (int i = 2; i < n; i++) {
hold[i] = Math.max(hold[i-1], unhold[i-2] - prices[i]);
unhold[i] = Math.max(unhold[i-1], hold[i-1] + prices[i]);
}
return Math.max(hold[n-1],unhold[n-1]);
}
}
同样是dp,状态由两个数组来维护:
hold[i]:第i天持有股票的最大收益;
unhold[i]:第i天未持有股票的最大收益。
状态转移方程:
hold[i] = Math.max(hold[i-1], unhold[i-1] - prices[i]);
unhold[i] = Math.max(unhold[i-1], hold[i-1] + prices[i] - fee);
交易的时候需要扣除手续费。
class Solution {
public int maxProfit(int[] prices, int fee) {
if (prices == null || prices.length == 0){
return 0;
}
int n = prices.length;
int[] hold = new int[n];
int[] unhold = new int[n];
hold[0] = -prices[0];
for (int i=1;i<n;i++){
hold[i] = Math.max(hold[i-1], unhold[i-1] - prices[i]);
unhold[i] = Math.max(unhold[i-1], hold[i-1] + prices[i] - fee);
}
return Math.max(unhold[n-1], hold[n-1]);
}
}
这个是在前面2笔交易的基础上做了扩展,变成k笔交易了。所以如果使用dp的话,笔数也需要作为状态参数考虑。
hold[k][i]:表示第i天第k次持有
unhold[k][i]:表示第i天第k次不持有
状态转移方程:
hold[i][j] = Math.max(hold[i][j-1], unhold[i-1][j-1] - prices[j]);
unhold[i][j] = Math.max(unhold[i][j-1], hold[i][j-1] + prices[j]);
初始化的时候,需要分析hold[i][0]、hold[0][i]、unhold[i][0]、unhold[0][i]的情况
class Solution {
public int maxProfit(int k, int[] prices) {
int n = prices.length;
if(n < 2 || k < 1){
return 0;
}
int[][] hold = new int[n][n];
int[][] unhold = new int[n][n];
int ans = 0;
//初始化
int min = prices[0];
for(int i=0;i<n;i++){
min = Math.max(min ,prices[i]);
hold[0][i] = -min;
}
int idx =0;
for(int i=1;i<n;i++){
if(prices[i] <= prices[idx]){
idx = i;
}
unhold[0][i] = Math.max(unhold[0][i-1], prices[i] - prices[idx]);
ans = Math.max(ans, unhold[0][i]);
}
for(int i=1;i<k;i++){
hold[i][0] = -1001;
unhold[i][0] = -1001;
}
for(int i=1;i<k;i++){
for(int j = 1;j<n;j++){
hold[i][j] = Math.max(hold[i][j-1], unhold[i-1][j-1] - prices[j]);
unhold[i][j] = Math.max(unhold[i][j-1],hold[i][j-1] + prices[j]);
ans = Math.max(ans, Math.max(hold[i][j], unhold[i][j]));
}
}
return ans;
}
}