如何買賣股票倦微?不要慌妻味,我有妙招!

Leetcode第121題到123題連續(xù)出現(xiàn)了三道買賣股票相關(guān)的題目欣福,一年前的網(wǎng)易筆試和半年前的百度面試都遇到過121題责球,不過不用慌,看完本文拓劝,你一定能夠完美解決買賣股票的問題雏逾。那么我們由易到難,依次介紹這三道題目郑临。

121. Best Time to Buy and Sell Stock

121題題目是這樣的:

在所有的過程中栖博,我們只允許一次的買賣,基于這個問題牧抵,我們得到了下面的兩種解法笛匙。

解法1

根據(jù)題意侨把,我們只需要找出數(shù)組中最大的差值即可,即 max(prices[j] – prices[i]) 妹孙,i < j秋柄。
如何得到最大的差值,只需要一次遍歷即可蠢正,在遍歷的用一個變量記錄遍歷到當前時的最小值即可骇笔。時間復(fù)雜度為O(n).
相關(guān)的實現(xiàn)代碼如下:

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length < 2) {
            return 0;
        }

        int min = prices[0];
        int profit = 0;

        // 第i天的價格可以看作是買入價也可以看作是賣出價
        for (int i = 1; i < prices.length; i++) {
            // 找到更低的買入價
            if (min > prices[i]) {
                // 更新買入價
                min = prices[I];
            } 
            // 當天的價格不低于買入價
            else {
                // 如果當天買出的價格比之前賣出的價格高
                if (profit < prices[i] - min) {
                    // 更新賣出價
                    profit = prices[i] - min;
                } 
            }
        }

        return profit;
    }
}

解法2

第二題的解法是我在面試百度的時候想到的,應(yīng)用的是求數(shù)組中和最大的連續(xù)子數(shù)組序列的思路嚣崭,這種思路又被稱為Kadane's Algorithm笨触。我們有兩個問題:

如何轉(zhuǎn)化為求數(shù)組中的和最大的連續(xù)子序列?相鄰兩個數(shù)作差即可雹舀,這樣的話子序列的和就是我們在子序列開始賣出股票芦劣,在子序列最后買回股票所能得到的收益。

那么什么是Kadane's Algorithm呢说榆?

kadane算法利用了數(shù)學歸納法的思想虚吟。簡單來講就是,隨意給你一個現(xiàn)成的數(shù)組签财,比如說?2, 1, ?3, 4, ?1, 2, 1, ?5, 4串慰,讓你求其中的最大子列和,并不是容易的事情唱蒸。但如果我們能從第一個數(shù)開始邦鲫,隨著數(shù)組的擴充,始終對其最大子列和保持跟蹤神汹,就可以輕易的求出任意一個數(shù)組的最大子列和庆捺。換言之,長度n的數(shù)組我們不會求慎冤,長度為一的總能算出來吧疼燥?長度為一的算出來了,二也就能算出來蚁堤,二算出來了醉者,三就能算出來,以此類推披诗,用這種根據(jù)i求i+1的思想撬即,我們就能達到最終目的。

詳細的分析一下呈队,往一個長度為i的數(shù)組后面插入第i+1個數(shù)剥槐,這時,數(shù)組的最大子列只有兩種情況宪摧,要么包括第i+1個數(shù)粒竖,要么不包括第i+1個數(shù)颅崩。即:

maxsubarraum = max(以第i+1個數(shù)結(jié)尾的子列和, 不以第i+1個數(shù)結(jié)尾的子列和)蕊苗。*

先計算前者沿后,以第i+1個數(shù)結(jié)尾的子列和怎么算呢?很簡單朽砰,要么它是以第i個數(shù)結(jié)尾的子列作為前綴尖滚,要么它不以之作為前綴。假設(shè)第i+1個數(shù)為x瞧柔,那么:

以第i+1個數(shù)結(jié)尾的子列和 = max(x漆弄,以第i個數(shù)結(jié)尾的子列和+x) (1)。

再計算后者造锅,也就是不以第i+1個數(shù)結(jié)尾的子列和撼唾。這啥意思呢?其實就是插入第i+1個數(shù)之前的數(shù)組的最大子列和嘛备绽。我們的數(shù)學歸納思想也就體現(xiàn)在這里券坞,如果你還看不明白,我們將*式改寫:

數(shù)列長度i+1的最大子列和 = max(以第i+1個數(shù)結(jié)尾的子列和肺素, 數(shù)列長度i的最大子列和)。(2)

看到了吧宇驾,無論(1)式還是(2)式倍靡,后一種情況都可以由前一種情況推出,妥妥的數(shù)學歸納课舍。我們的算法只要從i=1開始塌西,一步一步按照上面的規(guī)則走下去,那么任意一個數(shù)列的最大子列和就能求出來了筝尾!

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null || prices.length<2)
            return 0;
        int maxCur = 0;
        int maxSoFar = 0;
        for(int i=1;i<prices.length;i++){
            maxCur = Math.max(0,Math.max(prices[i]-prices[i-1],maxCur + prices[i] - prices[i-1]));
            maxSoFar = Math.max(maxCur,maxSoFar);
        }
        return maxSoFar;
    }
}

122. Best Time to Buy and Sell Stock II

這道題的描述如下:

這道題允許無限次的買賣捡需,簡直太簡單了吧,只要后一天的價值比前一天的大筹淫,那就買賣唄站辉。不忍吐槽的一道題,代碼如下:

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null || prices.length<2)
            return 0;
        int maxProf = 0;
        for(int i = 1;i<prices.length;i++){
            maxProf += (prices[i] > prices[i-1] ? prices[i] - prices[i-1]:0);
        }
        return maxProf;
    }
}

123. Best Time to Buy and Sell Stock III

這一題還是比較難的损姜,題目描述如下:

我們只允許最多兩次的買賣饰剥,這可如何是好?我們同樣提供兩種思路:

解法1
這個問題可以轉(zhuǎn)換成Best Time to Buy and Sell Stock I問題摧阅。

兩次股票交易的核心是可以定義一個交易點汰蓉,在這個交易點之前可以做一次交易(賺的最大數(shù)目的錢為firstProf),在這個交易點之后可以做一個交易(賺的最大數(shù)目的錢是secondProf)棒卷。那么要求的是max(firstProf+secondProf)顾孽。但是這個方法的時間復(fù)雜度是O(N^2)祝钢,空間復(fù)雜度是O(1)。leetcode中顯示超時若厚。

可以使用兩次掃描的方法避免上面的雙重循環(huán)拦英。

不同于Best Time to Buy and Sell Stock I中定義的初始狀態(tài)A[i]表示第i天賣出掙的最大數(shù)目的錢,這個更進一步直接定義A[i]表示前i天賺的最大數(shù)目的錢盹沈。minPrice表示從第0天到第i-1天中的最低價格龄章。
A[0]=0。(初始狀態(tài))
A[1]=max(prices[1]-prices[0],A[0])
A[2]=max(prices[2]-minPrice,A[1])
.....

即A[i]=max(price[i]-minPrice,A[i-1]).

另外一次掃描從數(shù)組后向前掃描乞封,定義B[i]表示從第i天到最后一天n能賺的最大數(shù)目的錢做裙。maxPrice表示第i+1天到n天的最高價格。
B[n]=0肃晚。(初始狀態(tài))
B[n-1]=max(maxPrice-prices[n-1],B[n])
B[n-2]=max(maxPrice-prices[n-2],B[n-1])
.....

即B[i]=max(maxPrice-prices[i],B[i+1])

那么以第i天為分割點能賺的最多數(shù)目的錢為A[i]+B[i]
問題的解為max{A[i]+B[i]}锚贱。0<=i<=n。
時間復(fù)雜度是O(N)关串,空間復(fù)雜度是O(N)拧廊。

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null || prices.length<2)
            return 0;
        int[] asc = new int[prices.length];
        int[] desc = new int[prices.length];
        int n = prices.length;
        int minprice = prices[0];
        int maxProf = 0;
        asc[0] = 0;
        for(int i=1;i<n;i++){
            asc[i] = Math.max(prices[i] - minprice,maxProf);
            minprice = Math.min(prices[i],minprice);
            maxProf = asc[i];
        }
        desc[prices.length-1] = 0;
        maxProf = 0;
        int maxprice = prices[prices.length-1];
        for(int i=prices.length-2;i>=0;i--){
            desc[i] = Math.max(maxprice-prices[i],maxProf);
            maxprice = Math.max(maxprice,prices[i]);
            maxProf = desc[i];
        }
        maxProf = 0;
        for(int i=0;i<prices.length;i++){
            maxProf = Math.max(maxProf,asc[i] + desc[i]);
        }
        return maxProf;
    }
}

解法2

第二種解法的核心是假設(shè)手上最開始只有0元錢,那么如果買入股票的價格為price晋修,手上的錢需要減去這個price吧碾,如果賣出股票的價格為price,手上的錢需要加上這個price墓卦。

因此我們定義了4個狀態(tài):
Buy1[i]表示前i天做第一筆交易買入股票后剩下的最多的錢倦春;
Sell1[i]表示前i天做第一筆交易賣出股票后剩下的最多的錢;
Buy2[i]表示前i天做第二筆交易買入股票后剩下的最多的錢落剪;
Sell2[i]表示前i天做第二筆交易賣出股票后剩下的最多的錢睁本;

那么假設(shè)我們在第i天時第二次賣出股票,我們賣出股票可以獲得Buy2[i-1]+prices[i]的錢忠怖,假設(shè)在第i天前已經(jīng)完成了兩筆交易呢堰,那么我們最多的錢是Sell2[i-1],因此
Sell2[i]=max{Sell2[i-1],Buy2[i-1]+prices[I]}
同樣的道理凡泣,假設(shè)我們在第i天時第二次買入股票枉疼,我們手中的錢是Sell[i-1]-prices[i],假設(shè)我們在第i天錢已經(jīng)賣出了兩次股票问麸,那么我們最多的錢是Buy2[i-1]往衷,因此
Buy2[i]=max{Buy2[i-1],Sell[i-1]-prices[I]}
同樣的道理我們還可以得到:
Sell1[i]=max{Sell[i-1],Buy1[i-1]+prices[I]}
Buy1[i]=max{Buy[i-1],-prices[I]}

可以發(fā)現(xiàn)上面四個狀態(tài)都是只與前一個狀態(tài)有關(guān),所以可以不使用數(shù)組而是使用變量來存儲即可严卖。

這是leetcode中的討論網(wǎng)址:https://leetcode.com/discuss/18330/is-it-best-solution-with-o-n-o-1

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null || prices.length<2)
            return 0;
        int sell2 = 0;
        int sell1 = 0;
        int buy1 = Integer.MIN_VALUE;
        int buy2 = Integer.MIN_VALUE;
        for(int i =0;i<prices.length;i++){
            sell2 = Math.max(sell2,buy2+prices[i]);
            buy2 = Math.max(buy2,sell1-prices[i]);
            sell1 = Math.max(sell1,buy1+prices[i]);
            buy1 = Math.max(buy1,-prices[i]);
        }
        return sell2;
    }
}

參考文獻

1席舍、從一道easy leetcode問題,談?wù)勛畲笞恿泻偷腒adane算法:http://blog.csdn.net/The__Apollo/article/details/77367534
2哮笆、LeetCode123:Best Time to Buy and Sell Stock III:http://blog.csdn.net/u012501459/article/details/46514309

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末来颤,一起剝皮案震驚了整個濱河市汰扭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌福铅,老刑警劉巖萝毛,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異滑黔,居然都是意外死亡笆包,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門略荡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來庵佣,“玉大人,你說我怎么就攤上這事汛兜“头啵” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵粥谬,是天一觀的道長肛根。 經(jīng)常有香客問我,道長漏策,這世上最難降的妖魔是什么派哲? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮掺喻,結(jié)果婚禮上狮辽,老公的妹妹穿的比我還像新娘。我一直安慰自己巢寡,他們只是感情好,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布椰苟。 她就那樣靜靜地躺著抑月,像睡著了一般。 火紅的嫁衣襯著肌膚如雪舆蝴。 梳的紋絲不亂的頭發(fā)上谦絮,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機與錄音洁仗,去河邊找鬼层皱。 笑死,一個胖子當著我的面吹牛赠潦,可吹牛的內(nèi)容都是我干的叫胖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼她奥,長吁一口氣:“原來是場噩夢啊……” “哼瓮增!你這毒婦竟也來了怎棱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绷跑,失蹤者是張志新(化名)和其女友劉穎拳恋,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體砸捏,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡谬运,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了垦藏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梆暖。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖膝藕,靈堂內(nèi)的尸體忽然破棺而出式廷,到底是詐尸還是另有隱情,我是刑警寧澤芭挽,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布滑废,位于F島的核電站,受9級特大地震影響袜爪,放射性物質(zhì)發(fā)生泄漏蠕趁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一辛馆、第九天 我趴在偏房一處隱蔽的房頂上張望俺陋。 院中可真熱鬧,春花似錦昙篙、人聲如沸腊状。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缴挖。三九已至,卻和暖如春焚辅,著一層夾襖步出監(jiān)牢的瞬間映屋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工同蜻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留棚点,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓湾蔓,卻偏偏與公主長得像瘫析,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

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