题目
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。注意:你不能在买入股票前卖出股票
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
解答
方法一:
我刚一开始分析这个题,觉得题目就是这样的特征
- 找到数组中两个数的差值最大
- 后边的数要比前边的数大
直接思路就是循环每一个元素,然后拿这个元素前边的元素和这个数挨个进行差值,找到最大值,返回的时候判断一下是否大于0
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.empty() || prices.size() == 1) {
return 0;
}
int res_max = prices[1] - prices[0];
for (int i = 2; i <prices.size(); ++i) {
for (int j = 0; j < i; ++j) {
if (prices[i] - prices[j] > res_max) {
res_max = prices[i] -prices[j];
}
}
}
return res_max > 0 ? res_max : 0;
}
};
一运行,超出时间限制,看来暴力方法越来越不让用了。
可是又不想放弃这个思路,我觉得这个很合理啊,一般第一反应大家都会这么想吧,为了得到提交结果,我想暴力方法肯定是多算了不少东西,看看尝试优化一下。
- 第一把默认最大值设为0,这样不用返回的时候每次进行一次三元运算,而且中间的数据判断碰到负数也不用进入if内部。
- 第二因为每个元素都会把之前的全部计算完一遍差值,那么在计算下一个元素的时候,如果下一个元素比这个元素小,是没有计算的必要的,因为小的话,假设这个元素位置最大差值变了,那么上一个元素比它大,肯定差值更大,那必然会提前计算出来,这个位置的计算也是没有必要的。加上这个判断应该能减少不少循环。
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.empty() || prices.size() == 1) {
return 0;
}
int res_max = 0;
for (int i = 1; i < prices.size(); ++i) {
///这种情况不要计算,因为如果i + 1 产生最大差值,
///那么 i的差值一定更大,肯定提前就计算完成了
if (prices[i] <= prices[i - 1]) {
continue;
}
for (int j = 0; j < i; ++j) {
if (prices[i] - prices[j] > res_max) {
res_max = prices[i] -prices[j];
}
}
}
return res_max;
}
};
果然这种方案没有超时。
方法二:
又想了一个方案看看能不能一次循环搞定。
- 标记一个买点位置,一个卖点位置,然后让他们移动
- 卖点位置一定在买点位置后边
- 如果出现比买点小的值,那么应该移动买点
- 如果出现比卖点大的值,那么应该移动卖点
- 每次都计算买点和卖点的差值,就可以找出最大值了
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.empty() || prices.size() == 1) {
return 0;
}
//假设差值最大的位置f_n
int res_max = prices[1] - prices[0];
int buy_index = 0, sell_index = 1;
for (int i = 1; i < prices.size(); i++) {
// 发现有比买点小的,就移动买点
if (prices[buy_index] > prices[i]) {
buy_index = i;
}
//发现有比卖点大的 就移动卖点
if (prices[sell_index] <= prices[i]) {
sell_index = i;
}
if (sell_index < buy_index) {
//买点移动之后,卖点也要移动
sell_index = buy_index;
continue;
}
res_max = max(res_max, prices[sell_index] - prices[buy_index]);
}
return res_max;
}
};
方法三:
看了其他人的方法得到的答案
思路应该大致和方法二相同,不过更简洁。
- 记录最小值,只要最小值有变化就更新最小值。
- 每一个元素都和最小值相减。比较大小。
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.empty() || prices.size() == 1) {
return 0;
}
int res_max = 0;
int min = prices[0]; //记录过往的最小值
for (int i = 1; i < prices.size(); i++) {
if (prices[i] > min) {
res_max = max(res_max, prices[i] - min);
} else {
min = prices[i];
}
}
return res_max;
}
};