Best Time to Buy and Sell Stock
題目等級:Easy
題目描述:
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 (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.
Note that you cannot sell a stock before you buy one.
Example 1:
Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Not 7-1 = 6, as selling price needs to be larger than buying price.
Example 2:
Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
題意:給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。最多只允許完成一筆交易(即買入和賣出一支股票),設計一個算法來計算你所能獲取的最大利潤。注意不能在買入股票前賣出股票。
解題思路:
對於本題,有以下兩種解法:
解法一:Brute Force暴力解法
由於限制只允許一次交易,那么一個最直觀的解法就是對每一天,都依次遍歷它之后的所有可能賣出的情況,記錄最大值,最后進行比較得出最終結果。很顯然這是一個二重循環,時間復雜度為O(n^2),空間復雜度:O(1)。
換句話說,這也就是將所有可能的買賣情況都窮舉出來,然后找最大值。
public int maxProfit(int[] prices) {
//解法一:蠻力法,找到每一天后續的最大值,比較確定最大利潤
//時間復雜度:O(n^2),空間復雜度:O(1)
if(prices==null || prices.length==0)
return 0;
int maxprofit=0;
for(int i=0;i<prices.length;i++){
for(int j=i+1;j<prices.length;j++){ //如果第i天買入,依次判斷它之后的每一天賣出的情況
if(prices[j]-prices[i]>maxprofit)
maxprofit=prices[j]-prices[i];
}
}
return maxprofit;
}
解法二:動態規划法
暴力法需要二重循環,解法二通過動態規划使得只需要一次遍歷即可找到最大值,動態規划適用於多階段決策過程的最優化問題,明顯這里就是一個決定什么時候買和賣出的階段決策問題。
如果我們用dp[i]表示從第1天到第i天進行一筆交易能獲得的最大收益,用min表示買入時的價格(最低的時候),則dp[i]=max(price[i]-min,dp[i-1])
,其中maxProfit是指已經找到的最大收益。
在求出所有的dp[i]以后我們再找到其中的最大值,即為所求值,由於只需要找到最大值,因此可以合二為一,遍歷的過程中順便求最大值,因此遞推公式變為:
dp[i]=max(price[i]-min,maxProfit)
由於只允許一次交易,所以要想獲得最大收益,必須在價格最低的時候買入,最高的時候賣出,但是由於必須先買后賣,所以如果用波形來說,就是要找到一個波峰和波谷,波谷在波峰之前。

在這里,我們只需要維護一個min變量,就可以省去一層循環,代碼如下:
public int maxProfit(int[] prices) {
//解法二:動態規划法:用dp[i]表示第i天賣出的收益,則dp[i]=max(price[i]-min,maxProfit)
//時間復雜度:O(n),空間復雜度:O(1)
if(prices==null || prices.length==0)
return 0;
int len=prices.length;
int maxprofit=0,min=Integer.MAX_VALUE;
for(int i=0;i<len;i++){
if(prices[i]<min) //維護一個最小值
min=prices[i];
else if(prices[i]-min>maxprofit)
maxprofit=prices[i]-min;
}
return maxprofit;
}
總結
以上兩種解法實際上都比較好理解,實際上,暴力法就是我們先確定哪一天買,這樣賣出的選擇就需要遍歷,而動態規划法,我們是先確定哪一天賣,由於題目的特點,那么買的時間就是賣之前的最小值,正好在這個遍歷的過程中我們就能把這個最小值記錄下來,因此得到了性能的提升。