1 BM80 买卖股票的最好时机(一)
描述
假设你有一个数组prices,长度为n,其中prices[i]是股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益
1.你可以买入一次股票和卖出一次股票,并非每天都可以买入或卖出一次,总共只能买入和卖出一次,且买入必须在卖出的前面的某一天;
2.如果不能获取到任何利润,请返回0;
3.假设买入卖出均无手续费;
示例1
输入:[8,9,2,5,4,7,1]
返回值:5
说明:在第3天(股票价格 = 2)的时候买入,在第6天(股票价格 = 7)的时候卖出,最大利润 = 7-2 = 5 ,不能选择在第2天买入,第3天卖出,这样就亏损7了;同时,你也不能在买入前卖出股票。
示例2
输入:[2,4,1]
返回值:2
示例3
输入:[3,2,1]
返回值:0
1.1 方法一:动态规划(O(n)、O(n))
//2.动态规划
//dp[i][0]第i天,不持股的最大收益。(不持股,可能一直没买,也可能之前已经卖出去了,或当天卖出的)
//dp[i][1]第i天,持股的最大收益。(持股,可能是之前买的,一直没卖;也可能当天买的,此时收益为-prices[i])
vector<vector<int>> dp(prices.size(),vector<int>(2,0));
//第一天没买,收益为0
dp[0][0] = 0;
//第一天买了,收益为-prices[0]
dp[0][1] = -prices[0];
for(int i = 1; i < prices.size(); i++){
//dp[i][0]第i天,不持股的最大收益
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
//dp[i][1]第i天,持股的最大收益
dp[i][1] = max(dp[i - 1][1], - prices[i]);
}
//return max(dp[prices.size() - 1][0],dp[prices.size() - 1][1]);
//不用在持有和不持有之间取max,因为最大利用时,一i当时卖出了,一定不持股
return dp[prices.size() - 1][0];
}
复杂度分析:
时间复杂度:O(n),其中n为数组长度,遍历一次数组
空间复杂度:O(n),动态规划辅助数组的空间
1.2 方法二:贪心((O(n)、O(1)))
//1.贪心策略
int maxProfit(vector<int>& prices) {
int res =0;//维持最大收益
int Min = prices[0];//首元为初始最小值
//贪心策略:MIn记录截至目前,历史最低价;;;res记录当天价格与历史最低的差值,取最大,即最大收益。
for(int i = 1; i < prices.size(); i++){
Min = min(Min,prices[i]);//维持最低价
res = max(res,prices[i] - Min);//维持最大收益
}
return res;
}
复杂度分析:
时间复杂度:O(n),其中nnn为数组长度,一次遍历数组;
空间复杂度:O(1),常数级变量,无额外辅助空间;
2 BM81 买卖股票的最好时机(二)
描述
假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益;
- 你可以多次买卖该只股票,但是再次购买前必须卖出之前的股票;
- 如果不能获取收益,请返回0;
- 假设买入卖出均无手续费;
示例1
输入:[8,9,2,5,4,7,1]
返回值:7
说明:
在第1天(股票价格=8)买入,第2天(股票价格=9)卖出,获利9-8=1
在第3天(股票价格=2)买入,第4天(股票价格=5)卖出,获利5-2=3
在第5天(股票价格=4)买入,第6天(股票价格=7)卖出,获利7-4=3
总获利1+3+3=7,返回7
示例2
输入:[5,4,3,2,1]
返回值:0
说明:由于每天股票都在跌,因此不进行任何交易最优。最大收益为0。
示例3
输入:[1,2,3,4,5]
返回值:4
说明:第一天买进,最后一天卖出最优。中间的当天买进当天卖出不影响最终结果。最大收益为4。
2.1 方法一:动态规划((O(n)、O(n)))
//dp[i][0],第i天,不持股的最大收益(可能一直没买,或之前或当天卖掉了)
//dp[i][1],第i天,持股的最大收益(一直没卖,或当天买入)
int n = prices.size();
vector<vector<int>> dp(n,vector<int>(2,0));
dp[0][0] = 0;//第一天不持股,收益0
dp[0][1] = -prices[0];//第一天买入,收益-prices[0]
for(int i = 1; i < n; i++){
dp[i][0] = max(dp[i-1][0],dp[i-1][1] + prices[i]);//前一天没有,今天也没买;或前一天有,今天卖了
dp[i][1] = max(dp[i-1][1],dp[i-1][0] - prices[i]);//前一天有,今天没卖;或前一天没有,今天买了
}
return dp[n-1][0];
}
复杂度分析:
时间复杂度:O(n),其中nnn为数组长度,遍历一次数组
空间复杂度:O(n),动态规划辅助数组相当于两个一维数组
2.2 贪心((O(n)、O(1)))
//2.贪心策略2:只要比前一天大,就立马买,不管什么区间
int n = prices.size();
int maxVal = 0;
for(int i = 1; i < n; i++){
if(prices[i-1] < prices[i])//这就意味着,卖的当天还可以买
maxVal += prices[i] - prices[i - 1];
}
return maxVal;
}
复杂度分析:
时间复杂度:O(n),其中n为数组长度,遍历一次数组
空间复杂度:O(1),常数级变量,没有使用额外辅助空间
2.3 贪心 (自己实现)((O(n)、O(1)))
int maxProfit(vector<int>& prices) {
//思路一:有收益时,这一段必定是单增的,当i+1下降时,卖掉i;同时,i+1更新为新的min
int n = prices.size();
int Min = prices[0];
int maxVal = 0;
int flag = true;//是否卖出
int i;
for(i = 1; i < n - 1; i++){
flag = true;
Min = min(prices[i],Min);
if(prices[i] > prices[i + 1]){ //一旦价格下降,就立马卖出;并且更新Min
maxVal += prices[i] - Min;
Min = prices[i + 1];
flag = false;
}
}
//如果最后一次存在利润,没有卖出或者单增一直都没卖
if(flag && prices[i] > Min) maxVal += prices[i] - Min;
return maxVal;
}
3 BM82 买卖股票的最好时机(三)
描述
假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益
- 你最多可以对该股票有两笔交易操作,一笔交易代表着一次买入与一次卖出,但是再次购买前必须卖出之前的股票
- 如果不能获取收益,请返回0
- 假设买入卖出均无手续费
示例1
输入:[8,9,3,5,1,3]
返回值:4
说明:
第三天(股票价格=3)买进,第四天(股票价格=5)卖出,收益为2
第五天(股票价格=1)买进,第六天(股票价格=3)卖出,收益为2
总收益为4。
示例2
输入:[9,8,4,1]
返回值:0
示例3
输入:[1,2,8,3,8]
返回值:12
说明:
第一笔股票交易在第一天买进,第三天卖出;第二笔股票交易在第四天买进,第五天卖出;总收益为12。
因最多只可以同时持有一只股票,所以不能在第一天进行第一笔股票交易的买进操作,又在第二天进行第二笔股票交易的买进操作(此时第一笔股票交易还没卖出),最后两笔股票交易同时在第三天卖出,也即以上操作不满足题目要求。
备注:
总天数不大于200000。保证股票每一天的价格在[1,100]范围内。
int maxProfit(vector<int>& prices) {
int n = prices.size();
vector<vector<int>> dp(n,vector<int>(5,-10000));//初始值为何-10000?,初始为0,运行错误
dp[0][0] = 0;
dp[0][1] = -prices[0];
for(int i = 1; i < n; i++){
dp[i][0] = dp[i-1][0];
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);//第一次持股
dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i]);//第一次卖出
dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i]);//第二次持股
dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i]);//第二次卖出
}
//选取最大值,可以只操作一次
return max(dp[n-1][2],max(0,dp[n-1][4]));
}
复杂度分析:
时间复杂度:O(n),其中nnn为数组长度,只遍历一次数组
空间复杂度:O(n),动态规划二维辅助相当于5个一维数组