Best Time to Buy and Sell Stock I II III IV

1、Best Time to Buy and Sell Stock I

Description: Say you have an array for which the ith element is the price of a given stock on day i. If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.

题意:用一个数组表示股票每天的价格,数组的第i个数表示股票在第i天的价格。 如果只允许进行一次交易,也就是说只允许买一支股票并卖掉,求最大的收益。

分析:动态规划法。从前向后遍历数组,记录当前出现过的最低价格,作为买入价格,并计算以当天价格出售的收益,作为可能的最大收益,整个遍历过程中,出现过的最大收益就是所求。

代码:O(n)时间,O(1)空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public int maxProfit(int[] prices) {
if(prices.length==0){
return 0;
}
int re=0;
int min=prices[0];

for(int i=1; i<prices.length; i++){
int diff=prices[i]-min;
if(diff>0){
re=Math.max(re, diff);
}
min=Math.min(min, prices[i]);
}
return re;
}

2、Best Time to Buy and Sell Stock II

Description: Say you have an array for which the ith element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

题目:用一个数组表示股票每天的价格,数组的第i个数表示股票在第i天的价格。交易次数不限,但一次只能交易一支股票,也就是说手上最多只能持有一支股票,求最大收益。

分析:贪心法。从前向后遍历数组,只要当天的价格高于前一天的价格,就算入收益。

代码:时间O(n),空间O(1)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int maxProfit2(int[] prices) {
if(prices.length==0){
return 0;
}
int re=0;
int last=prices[0];
for(int i=1; i<prices.length; i++){
if(prices[i]>last){
re+=prices[i]-last;
}
last=prices[i];
}
return re;
}

3、Best Time to Buy and Sell Stock III

Description: Say you have an array for which the ith element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete at most two transactions. Note: You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

题意:用一个数组表示股票每天的价格,数组的第i个数表示股票在第i天的价格。最多交易两次,手上最多只能持有一支股票,求最大收益。

分析:动态规划法。以第i天为分界线,计算第i天之前进行一次交易的最大收益pre[i],和第i天之后进行一次交易的最大收益post[i],最后遍历一遍,max{pre[i] + post[i]} (0≤i≤n-1)就是最大收益。第i天之前和第i天之后进行一次的最大收益求法同Best Time to Buy and Sell Stock I。

代码:时间O(n),空间O(n)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public int maxProfit3(int[] prices) {
if(prices.length<2){
return 0;
}
int len=prices.length;
int[] pre=new int[len];
int[] post=new int[len];

int min=prices[0];
for(int i=1; i<len; i++){
min=Math.min(min, prices[i]);
pre[i]=Math.max(pre[i-1], prices[i]-min);
}
int max=prices[len-1];
for(int i=len-2; i>=0; i--){
max=Math.max(max, prices[i]);
post[i]=Math.max(post[i+1], max-prices[i]);
}

int re=0;
for(int i=0; i<len; i++){
re=Math.max(re, pre[i]+post[i]);
}

return re;
}

4、Best Time to Buy and Sell Stock IV

Description: Say you have an array for which the ith element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete at most k transactions. Note: You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

题意:用一个数组表示股票每天的价格,数组的第i个数表示股票在第i天的价格。最多交易k次,手上最多只能持有一支股票,求最大收益。

分析:特殊动态规划法。传统的动态规划我们会这样想,到第i天时进行j次交易的最大收益,要么等于到第i-1天时进行j次交易的最大收益(第i天价格低于第i-1天的价格),要么等于到第i-1天时进行j-1次交易,然后第i天进行一次交易(第i天价格高于第i-1天价格时)。于是得到动规方程如下(其中diff = prices[i] – prices[i – 1]):

profit[i][j] = max(profit[i – 1][j], profit[i – 1][j – 1] + diff)

看起来很有道理,但其实不对,为什么不对呢?因为diff是第i天和第i-1天的差额收益,如果第i-1天当天本身也有交易呢(也就是说第i-1天刚卖出了股票,然后又买入等到第i天再卖出),那么这两次交易就可以合为一次交易,这样profit[i – 1][j – 1] + diff实际上只进行了j-1次交易,而不是最多可以的j次,这样得到的最大收益就小了。

那么怎样计算第i天进行交易的情况的最大收益,才会避免少计算一次交易呢?我们用一个局部最优解和全局最有解表示到第i天进行j次的收益,这就是该动态规划的特殊之处。

用local[i][j]表示到达第i天时,最多进行j次交易的局部最优解;用global[i][j]表示到达第i天时,最多进行j次的全局最优解。它们二者的关系如下(其中diff = prices[i] – prices[i – 1]):

local[i][j] = max(global[i – 1][j – 1] , local[i – 1][j] + diff)
global[i][j] = max(global[i – 1][j], local[i][j])

local[i][j]和global[i][j]的区别是:local[i][j]意味着在第i天一定有交易(卖出)发生,当第i天的价格高于第i-1天(即diff > 0)时,那么可以把这次交易(第i-1天买入第i天卖出)跟第i-1天的交易(卖出)合并为一次交易,即local[i][j]=local[i-1][j]+diff;当第i天的价格不高于第i-1天(即diff<=0)时,那么local[i][j]=global[i-1][j-1]+diff,而由于diff<=0,所以可写成local[i][j]=global[i-1][j-1]。global[i][j]就是我们所求的前i天最多进行k次交易的最大收益,可分为两种情况:如果第i天没有交易(卖出),那么global[i][j]=global[i-1][j];如果第i天有交易(卖出),那么global[i][j]=local[i][j]。

代码:时间O(nk),空间O(nk)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public int maxProfit(int k, int[] prices) {
if(prices.length<2){
return 0;
}
int len=prices.length;
if(k>=len){
return maxProfit2(prices);
}

int[][] local=new int[len][k+1];
int[][] global=new int[len][k+1];

for(int i=1; i<len; i++){
int diff=prices[i]-prices[i-1];
for(int j=1; j<=k; j++){
local[i][j]=Math.max(local[i-1][j]+diff, global[i-1][j-1]);
global[i][j]=Math.max(local[i][j], global[i-1][j]);
}
}

return global[len-1][k];
}

补充:这道题还有一个陷阱,就是当k大于天数时,其实就退化成 Best Time to Buy and Sell Stock II 了。就不能用动规来做了,为什么?(请思考) 另外,Best Time to Buy and Sell Stock III 就是本题k=2的情况,所以说IV是II和III的综合。

[转自来自梁佳宾的网络日志][http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html]