LeetCode 121. Best Time to Buy and Sell Stock

Leet Code 題目 121: Best Time to Buy and Sell Stock

題目描述

題目解釋:你可以買一次股票跨琳、賣一次股票粘拾,你已經(jīng)知道該股票在每一天的價格鸵膏,請算出來只能買一次纳猪、賣一次股票陋葡,可獲得的最大利潤為何棋傍?例如:[7,1,5,3,6,4] 在 1 的時候買入救拉,6 的時候賣出,可獲得最大利潤為 5瘫拣。

Step 1: 新增一個測試用例近上,prices 長度為 2,no transaction done拂铡。

【測試代碼】prices 為 {5,4}, 因為怎麼買賣都不會賺錢壹无,所以 max profit 為 0。

prices 長度為 2感帅,沒有交易的測試用例

【生產(chǎn)代碼】?

尚未實現(xiàn)的生產(chǎn)代碼

Step 2: hard-code 判斷前者比後者大斗锭,則回傳0。

【生產(chǎn)代碼】如果 prices[1]prices[0] 小失球,回傳 0岖是。

hard-code 判斷後,回傳 0

【重構(gòu)測試代碼】?擷取方法实苞,將 assertion 行為抽取到 AssertMaxProfitShouldBe()

重構(gòu)測試:擷取驗證行為

Step 3: 新增測試用例豺撑,prices 長度仍為 2,後者比較大黔牵,回傳差值聪轿。

【測試代碼】

長度為 2, 應(yīng)回傳差值的測試用例

【生產(chǎn)代碼】?hard-code 判斷當(dāng)後者比前者大時,回傳兩者差值猾浦,即為 max profit陆错。

hard-code 判斷,回傳差值

Step 4: 新增測試用例金赦,prices 長度為 3, 最大值為最後一位音瓷。

【測試代碼】?預(yù)期會失敗,因為期望結(jié)果為 3夹抗,目前實現(xiàn)的生產(chǎn)代碼實際結(jié)果為 2绳慎。強(qiáng)制驅(qū)動生產(chǎn)代碼設(shè)計,當(dāng)數(shù)字長度不只 2 位時漠烧,該怎麼處理杏愤。

長度為3,且最大值為最後一位的測試用例

【生產(chǎn)代碼】?透過 Skip(1) 取得剩下的 prices, 並且取其中最大值沽甥,與目前比較的 price 取差值声邦。(仍屬 hard-code 階段)

取剩餘 prices 的最大值,來進(jìn)行差值計算

Step 5: 新增通過的測試用例

【新增預(yù)期通過的測試代碼】?prices{4,7,6} 也可以符合 remainPrices 與取 Max() 差值的需求摆舟。因為比較點仍在 prices[0] 的 4。

prices 為 {4,7,6} 的測試用例

【新增預(yù)期通過的測試代碼】?即使 prices 長度不只是 3, 只要 prices[0] 是比較值,目前的生產(chǎn)代碼就能滿足此需求恨诱。

prices 為 {4,7,6,9,3} 的測試用例

我的 TDD 習(xí)慣媳瞪,當(dāng) baby step 的內(nèi)容比較複雜一點且變成綠燈時,我會多寫幾個有代表性的測試案例來確保目前實現(xiàn)的生產(chǎn)代碼符合我的預(yù)期照宝。

Step 6: 新增測試用例蛇受,prices[1] < prices[0],prices 長度為 3

【測試代碼】?強(qiáng)制驅(qū)動生產(chǎn)代碼的第一個 if block 進(jìn)行修改厕鹃,擺脫 hard-code prices[1] 的部分兢仰。因為原本生產(chǎn)代碼的實現(xiàn)會回傳 0,但這個測試案例預(yù)期回傳為 6剂碴,所以會驗證失敗把将。

prices 為 {4,1,7} 的測試用例

【生產(chǎn)代碼】?類似 Step 4 的方式處理,只是 flag 改成 prices[1]忆矛。

生產(chǎn)代碼迭代差異

【重構(gòu)】?將 if/else block 中察蹲,duplicate 的部分抽取到 if/else block 之外。

move duplicate code out of if/else block

Step 7: 重構(gòu) algorithm, 改成遞迴處理

【生產(chǎn)代碼】?每一個 price 都跟剩餘的 prices[] 中的最大值做差值催训,保留最大差值即為 max profit洽议。

    public class Solution
    {
        public int MaxProfit(int[] prices)
        {
            return FindMaxProfitFromNextPrice(prices[0], prices.Skip(1), 0);
        }

        private int FindMaxProfitFromNextPrice(int flag, IEnumerable<int> remainPrices, int lastMaxProfit)
        {
            if (!remainPrices.Any())
            {
                return lastMaxProfit;
            }

            var max = remainPrices.Max();
            var currentMaxProfit = max - flag;
            var maxProfit = Math.Max(lastMaxProfit,
 currentMaxProfit);
            return FindMaxProfitFromNextPrice(remainPrices.ElementAt(0), remainPrices.Skip(1), maxProfit);
        }
    }

到目前為止,需求所需商業(yè)邏輯已經(jīng)完整實現(xiàn)漫拭,但很遺憾的亚兄,無法通過 LeetCode 上效能的測試案例。

Time Limit Exceeded

腦袋想了幾種常見的 algorithm 方式做調(diào)整采驻,但都還是失敗了儿捧。最後還是上網(wǎng)找了一下,才知道這一題 LeetCode 其實是 **max subarray problem **的變形題挑宠。從 wiki 中得知菲盾,可採用 Kadane's algorithm 來處理,讓時間複雜度降到 O(n)各淀。

Step 8: Kadane's algorithm 的實現(xiàn)

【生產(chǎn)代碼】?Kadane's algorithm 說明:

  1. 一串?dāng)?shù)列的最大差值懒鉴,其實是差值的總和(這邊的 currentSum),直到滿足第二點的條件碎浇,代表數(shù)列結(jié)束临谱。例如:{4,6,7} 最大差值的 7 - 4 = 3, 可以被轉(zhuǎn)成 (6-4) + (7-6) = 3
  2. 當(dāng) currentSum < 0 時,代表截至目前為止奴璃,這一段最大總和已決定悉默。所以要 reset 為 0, 以便計算下一段的最大差值。
  3. maxSum 用來存放每一段的最大差值苟穆。
    public class Solution
    {
        public int MaxProfit(int[] prices)
        {
            if (prices.Length < 2)
            {
                return 0;
            }

            return FindMaxProfitByKadane(prices);
        }

        private int FindMaxProfitByKadane(int[] prices)
        {
            var currentSum = 0;
            var maxSum = 0;
            for (int i = 1; i < prices.Length; i++)
            {
                currentSum = Math.Max(0, currentSum += prices[i] - prices[i - 1]); //less than 0, reset to 0
                maxSum = Math.Max(maxSum, currentSum);
            }

            return maxSum;
        }
    }

通過 LeetCode 上所有測試用例

pass all test cases at LeetCode

結(jié)論

練習(xí)這一題 LeetCode 抄课,我的心路歷程:

  1. 有趣的題目唱星,有感,看起來有點難又不會太難
  2. TDD 一下練手感
  3. 抽象跟磨,淬取出遞迴的方式
  4. 絞盡腦汁间聊,碰到無法突破的效能瓶頸
  5. 上網(wǎng)找相關(guān)解法,瞭解到針對這特殊需求的處理方式抵拘。原來這樣的需求用這樣的 algorithm 是可以巧妙解決的哎榴。多開的地圖包含:Maximum subarray problem, Kadane's algorithm, 動態(tài)規(guī)劃碍扔,其實現(xiàn)方式酸些、原理與使用場景。

說真的磕蒲,這一題如果拿來當(dāng)面試考題充尉,我覺得可以寫出「易讀且可滿足完整需求」的虛擬碼就剛好了飘言,Kadane's algorithm 的解法當(dāng)交朋友閒聊或題目補(bǔ)充就夠了。

GitHub commit history: LeetCode_121_BestTimeBuyAndSellStock

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末喉酌,一起剝皮案震驚了整個濱河市热凹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌泪电,老刑警劉巖般妙,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異相速,居然都是意外死亡碟渺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門突诬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苫拍,“玉大人,你說我怎么就攤上這事旺隙∪藜” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵蔬捷,是天一觀的道長垄提。 經(jīng)常有香客問我,道長周拐,這世上最難降的妖魔是什么铡俐? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮妥粟,結(jié)果婚禮上审丘,老公的妹妹穿的比我還像新娘。我一直安慰自己勾给,他們只是感情好滩报,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布锅知。 她就那樣靜靜地躺著,像睡著了一般露泊。 火紅的嫁衣襯著肌膚如雪喉镰。 梳的紋絲不亂的頭發(fā)上旅择,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天惭笑,我揣著相機(jī)與錄音,去河邊找鬼生真。 笑死沉噩,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的柱蟀。 我是一名探鬼主播川蒙,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼长已!你這毒婦竟也來了畜眨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤术瓮,失蹤者是張志新(化名)和其女友劉穎康聂,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胞四,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡恬汁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了辜伟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氓侧。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖导狡,靈堂內(nèi)的尸體忽然破棺而出约巷,到底是詐尸還是另有隱情,我是刑警寧澤旱捧,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布独郎,位于F島的核電站,受9級特大地震影響廊佩,放射性物質(zhì)發(fā)生泄漏囚聚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一标锄、第九天 我趴在偏房一處隱蔽的房頂上張望顽铸。 院中可真熱鬧,春花似錦料皇、人聲如沸谓松。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鬼譬。三九已至娜膘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間优质,已是汗流浹背竣贪。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留巩螃,地道東北人演怎。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像避乏,于是被迫代替她去往敵國和親爷耀。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容