本專題旨在分享刷Leecode過程發現的一些思路有趣或者有價值的題目。【當然是基於js進行解答】。
動態規劃一樣是leetcode 中等難度習題的重點型別之一,同時可能也是面試熱點之一,所以重要性不言而喻。
題目相關
- 原題地址: https://leetcode-cn.com/problems/dui-cheng-de-er-cha-shu-lcof/
題目描述:
示例1:
輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。 (注意利潤不能是 7-1 = 6, 因為賣出價格需要大於買入價格。)
- 示例2:
輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。
- 限制:- <= 陣列長度 <= 10^5
思路解析
暴力破解
首先有一部分同學喜歡上來就來一波暴力破解,直接計算出所有可能的組合情況:
找出給定陣列中兩個數字之間的最大差值(即,最大利潤)。此外,第二個數字(賣出價格)必須大於第一個數字(買入價格), 那麼所有的組合情況就是n*(n-1)/2
,複雜度是O(n^2),毫無疑問,在題目給定的0 <= 陣列長度 <= 10^5
下,妥妥超時; 而且面試如果暴力破解的話,估計也要被暴力pass了。
動態規劃
(這個名字一聽就很科學!)
其實早期詳細寫過動態規劃的基礎思路:https://segmentfault.com/a/11...
建議不熟悉的同學可以先去看看這一篇。
而所謂動態規劃,核心就是就是把多階段過程,轉化為一系列單階段問題;把問題不斷拆解為子問題去求解。
如果看過基礎篇的同學應該知道,這裡說的拆解,其實更直白一些就是找到第i個子問題與前i個子問題的關係。
帶入本道題其實就是 把求解n天裡買賣股票最高利潤的問題,先轉化為先求解第n天賣出股票時的最高利潤,然後從中找出最大值即可。
以輸入 [7,1,5,3,6,4]
為例:
- 第1天賣出的話,最高利潤是0,等於是無交易;
- 第2天賣出的話,最高利潤是0,因為第二天價格是1,高出第1天,也不會交易;
- 第3天賣出的話,的最高利潤是4,也就是第二天買入,第三天賣出;
- 以此類推...
來觀察計算求解第i天賣出股票時,所能得到的最高利潤的核心,只要已知目前為止的歷史最低價,那麼前i天的最高利潤其實就是 用第i天的價格減去這個歷史最低價即可,那麼思路不就來了?
var maxProfit = function(prices) {
const profit = []; // profit[i] 表示第i天賣出時的最大利潤
let historyMinPrice = Infinity;
for(let i = 0; i <= prices.length; i++) {
// 保持更新迄今為止的歷史最低價
if(prices[i] < historyMinPrice) {
historyMinPrice = prices[i];
}
profit[i] = prices[i] - historyMinPrice;
}
// 未完待續
// ...
};
那麼,有了這個price
陣列之後, 只要獲取其中的最大值,其實也就是題目所要求的輸出了。 這個想必難不倒大家,可以直接迴圈比對一次獲得,但是其實思考下,並沒必要再進行一次迴圈,因為在原來的迴圈過程,我們其實就可以沿用歷史最低價的思路, 另外維持一個目前為止的最大利潤變數。 也就是
var maxProfit = function(prices) {
const profit = []; // profit[i] 表示第i天賣出時的最大利潤
let historyMinPrice = Infinity;
let maxProfit = 0;
for(let i = 0; i <= prices.length; i++) {
// 保持更新迄今為止的歷史最低價
if(prices[i] < historyMinPrice) {
historyMinPrice = prices[i];
}
profit[i] = prices[i] - historyMinPrice;
if(profit[i] > maxProfit) {
maxProfit = profit[i];
}
}
return maxProfit;
};
還可以更優嗎
現在我們已經解出了這道題,那麼就到此為止了嗎? 回頭看看我們的程式碼,會發現是否有必要維持profit陣列的存在呢? 它的意義僅用於更新maxProfit
而已 那麼是不是...
大膽一點! 直接把它移除掉!!
var maxProfit = function(prices) {
// 幹掉profit[i]
let historyMinPrice = Infinity;
let maxProfit = 0;
for(let i = 0; i <= prices.length; i++) {
// 保持更新迄今為止的歷史最低價
if(prices[i] < historyMinPrice) {
historyMinPrice = prices[i];
}
if(prices[i] - historyMinPrice > maxProfit) {
maxProfit = prices[i] - historyMinPrice;
}
}
return maxProfit;
};
到這裡是不是成就感更深了一層!
那麼本題的解答也就到此結束了,下期再見!