做者 | 王磊java
來源 | Java中文社羣(ID:javacn666)git
本文已收錄至 Github《小白學算法》系列:https://github.com/vipstone/algorithgithub
今天螞蟻集團(支付寶)正式上市了,毫無疑問這一舉措又造就了一大批富豪,然而做爲局外人的咱們,也只有羨慕的份了。明明能夠靠運氣吃飯,咱非得靠實力,你說虧不虧啊?web
但話又說回來,能進螞蟻的人也都是牛人,那咱也趕忙提高一下技能吧,好爲下一個「螞蟻」作足準備。面試
今天的這道題比較有意思,是關於「買賣股票」的,題目以下。算法
題目描述
給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。若是你最多隻容許完成一筆交易(即買入和賣出一支股票一次),設計一個算法來計算你所能獲取的最大利潤。數組
注意:你不能在買入股票前賣出股票。微信
示例 1:app
輸入: [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。
來源:LeetCode
劍指 offer 64:https://leetcode-cn.com/problems/gu-piao-de-zui-da-li-run-lcof/submissions/難度:中
leetcode 121:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/難度:簡單
解題思路
根據題目的意思咱們知道,咱們只有一次交易的機會,也就是買一次再賣一次,但同時要保證收益最大化。那咱們本能的直覺是在最低的價格買入,再在最高的價格賣出就行了,以下圖所示:

但這有一個問題,就是咱們要保證最高價格要在最低的價格以後,由於咱們必須在購買了股票以後才能賣出,而不是相反的順序,這就讓問題變的複雜了。
但此刻咱們想到了一個最直接也是最笨的一個方法,那就是用暴力窮舉法,咱們使用兩層循環,依次在每一個節點買入,而後再在以後的全部節點賣出,這樣來計算節點間的差值(收益),若是此差值大於當前最高收益,就將此值設置爲當前最高收益,這樣循環完,咱們就能得到最大收益了。以下圖所示:

方法一:暴力法
有了上面的思路,接下來咱們用代碼實現一下:
class Solution {
public int maxProfit(int[] prices) {
int mp = 0; // 最高收益
for (int i = 0; i < prices.length; i++) {
for (int j = i + 1; j < prices.length; j++) {
int item = prices[j] - prices[i];
if (item > mp) mp = item;
}
}
return mp;
}
}
能夠看出代碼仍是很簡單的,但別高興得太早,咱們來看它在 leetcode 上的表現:
複雜度分析
-
時間複雜度:O(n^2),循環運行 n(n-1)/2 次。 -
空間複雜度:O(1),只使用了常數位的變量。
真是一頓操做猛如虎,最終擊敗百分之五!若是用這種代碼去面試的話,估計只能回去等通知了。那有沒有更好的方法呢?答案是確定的,繼續往下看。
方法二:遍歷一次
對於這道題咱們其實能夠使用一次循環來實現,先來看下面的這張折線圖:

從上面的圖片咱們能夠看出,咱們在每一個節點其實只會作兩件事(第一個節點除外,只能買入不能賣出),這兩件事分別是:買入或賣出。那麼咱們其實能夠用一個循環來計算出最大的利潤,咱們只須要依次對於每一個節點作如下兩個判斷:
-
判斷當前節點是否是相對最低價,若是是,則將它設置爲最低價(也就是買入); -
若是當前節點不是最低價,那咱們就將它賣出,而後計算賣出的收益(當前節點減去相對最低價),若是賣出的收益大於目前的最高收益,則將此值設置爲最高收益。
這樣循環完成以後最高收益就出來了,實現代碼以下:
class Solution {
public int maxProfit(int prices[]) {
if (prices == null || prices.length == 0) return 0;
int mp = 0; // 最高收益
int min = prices[0]; // 最低價
for (int i = 1; i < prices.length; i++) {
if (prices[i] < min) {
// 設定相對最低價
min = prices[i];
} else if (mp < (prices[i] - min)) {
// 設定最高盈利
mp = prices[i] - min;
}
}
return mp;
}
}
以上代碼在 leetcode 中的結果以下圖所示:
複雜度分析
-
時間複雜度:O(n),只須要遍歷一次。 -
空間複雜度:O(1),只使用了常數個變量。
從以上的執行的結果能夠看出,這段代碼還算是比較理想的,這樣面試官也會對你豎起大拇指了。
總結
本文咱們計算了單次(一次買入和賣出)股票的最高收益,剛開始咱們使用的是最簡單粗暴的暴力枚舉法,使用兩層循環依次相減來求出最高收益值,但這種方法的執行效率過低。
而後咱們通過觀察折線圖發現,只須要一次循環也能找出最高的收益值。咱們只須要在每一個節點作兩個判斷,第一:判斷此節點是否爲相對最小值,若是是,則記錄下來;若是不是,則計算此值和相對最小值是否爲當前最高收益,若是是,則記錄下來。那麼循環一圈以後,咱們就能得出最高的收益了,而且執行的效率也很高。
你學會了嗎?有不懂的地方或者更好的方法,歡迎評論區留言~

往期推薦
2020-10-26

2020-10-24

2020-10-21

本文分享自微信公衆號 - Java中文社羣(javacn666)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。