題目
給定一個數組,它的第 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;
}
};