題目描述
給定一個陣列 prices
,它的第 i
個元素 prices[i]
表示一支給定股票第 i
天的價格。
你只能選擇 某一天 買入這隻股票,並選擇在 未來的某一個不同的日子 賣出該股票。設計一個演算法來計算你所能獲取的最大利潤。
返回你可以從這筆交易中獲取的最大利潤。如果你不能獲取任何利潤,返回 0
。
示例 1:
輸入:[7,1,5,3,6,4]
輸出:5
解釋:在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。
注意利潤不能是 7-1 = 6, 因為賣出價格需要大於買入價格;同時,你不能在買入前賣出股票。
示例 2:
輸入:prices = [7,6,4,3,1]
輸出:0
解釋:在這種情況下, 沒有交易完成, 所以最大利潤為 0。
力扣原題目地址:https://leetcode.cn/problems/...
思路分析
關於這道題目,可以兩次迴圈暴力求結果,不過不太好,此處不說暴力求解結果的程式碼方式(附在最後)
。
我們一般遇到一個複雜問題的時候,可以把複雜問題進行簡化拆解
,在解決簡化拆解問題的時候,尋找規律、發現規律,最終在複雜問題上使用我們之前發現的簡化問題相似的規律
,從而解決之
比如老闆上來就讓我們製造一個原子彈,啊,不會啊。沒關係我們先研究一下,如何製造一個手榴彈。反正都是蛋嘛,都是爆炸用的,總是有相通之處,手榴彈製造出來了,原子彈也就快了。這個比喻不太恰當,反正是那個意思,大家自行體會哈 ^_^
閱讀題目以後我們大概知道需求如下:
要想獲得最高收益,就必須在低位買進、高位賣出
- 即:
找到陣列中的最大值,最小值,最大減最小就是最高收益
- 即:
股票是,先買後賣。所以買股票的時間靠前、賣股票的時間靠後
- 即:
最小值的位置索引要小於最大值的位置索引
- 即:
- 問題是,我們暫時不知道那天買進股票最低,那天最高???
乾脆,我們就假設,頭一天股票最低,3塊錢就能買,後面股票漲漲跌跌,但是,始終沒有低於3塊的。所以頭一天買進最划算。我們以5天為週期,刷卡付款頭一天買了。於是就模擬定義一個陣列:let arr = [3,8,6,12,9]
,接下來,我們把問題轉化成:
已知第一天買的股價最低,後面的每一天都比第一天股價高,求那一天賣掉最多能賺多少錢
複雜問題拆解之簡化
於是我們根據需求,可以寫出下面的程式碼:
let arr = [3, 8, 6, 12, 9]
function maxProfit(prices) {
// 1. 假設初始收益為0,後面每一天的價格減去第一天的買入價格就是收益
let maxProfitValue = 0
for (let i = 1; i < prices.length; i++) {
// 2. 只要收益大的(收益大的記在本子上,收益小的劃掉不要)
maxProfitValue = Math.max(maxProfitValue, prices[i] - 3)
}
// 3. 最終得到一個最大收益,並返回
return maxProfitValue;
}
console.log( maxProfit(arr) ); // 9
上述程式碼 maxProfitValue = Math.max(maxProfitValue, prices[i] - 3)
我們可以換一下寫法,因為3
是第一天的價格,所以換成arr[0]
即:
複雜問題拆解之簡單變換
let arr = [3, 8, 6, 12, 9]
function maxProfit(prices) {
let min = prices[0] // 變換之~我們已知最低點股價是第一項,故定義變數表示
// 1. 假設初始收益為0,後面每一天的價格減去第一天的買入價格就是收益
let maxProfitValue = 0
for (let i = 1; i < prices.length; i++) {
// 2. 只要收益大的(收益大的記在本子上,收益小的劃掉不要)
maxProfitValue = Math.max(maxProfitValue, prices[i] - min) // 變換之~最低價是min,這裡賣出高價-買入最低價也是用min變數了
}
// 3. 最終得到一個最大收益,並返回
return maxProfitValue;
}
console.log( maxProfit(arr) ); // 9
看到這裡,有的朋友說了,假如第一天、陣列中的第一項不是最小的呢?假如第二項有可能比第一項小呢?沒關係,我們們對比一下就行了,誰小用誰。反正有迴圈,可以拿到每一項的值,對比就是嘍。虛擬碼如下:
let min = arr[0] // 假設第一項最小
min = Math.min(min,arr[i]) // 第二項和當下最小值做對比,誰小就保留誰
Math.min(5,2,8,3) // Math.min求一組數中的最小值 左側程式碼執行得到結果2
於是上述程式碼又可以做一個變換
複雜問題之變換解決之
function maxProfit(prices) {
let min = prices[0] // 1. 假設最低股價是第一項,若後續有更低的股價,就用後續更低的
let maxProfitValue = 0 // 2. 假設初始收益為0,後面每一天的價格減去買入價格就是收益
for (let i = 1; i < prices.length; i++) {
min = Math.min(min, prices[i]) // 3. 變換解決之,使用Math.min對比一下,看那一天的股價最低,就用那個
// 4. 只要收益大的(收益大的記在本子上,收益小的劃掉不要)
maxProfitValue = Math.max(maxProfitValue, prices[i] - min) // 5. 已知最低價是min,那麼收益就等於賣出價-最低價
}
return maxProfitValue; // 6. 最終得到一個最大收益,並返回
}
好了,看到這裡,力扣演算法題也就解決啦。所以思路很重要:複雜的問題簡單化,在簡單化處理的過程中不斷尋找規律,從而向複雜化過渡,最終通過尋得的規律解決複雜化的問題
附:雙層迴圈暴力解法
function maxProfit(prices) {
let arr = [0] // 指定預設收益為0
for (let i = 0; i < prices.length; i++) {
for (let j = i + 1; j < prices.length; j++) {
arr.push(prices[j] - prices[i]) // 把所有計算出的利潤都追加到一個陣列中
}
}
return Math.max(...arr) // 然後求出這個陣列中的最大值即為最大收益
}