1. 程式人生 > >算法系列-動態規劃(4):買賣股票的最佳時機

算法系列-動態規劃(4):買賣股票的最佳時機

此係列為動態規劃相關文章。 系列歷史文章: [算法系列-動態規劃(1):初識動態規劃](https://mp.weixin.qq.com/s/YhbOi2_LInQ7EXP3WDgodA) [算法系列-動態規劃(2):切割鋼材問題](https://mp.weixin.qq.com/s/TsX5dkKEmJn9Qup0__4AAg) [算法系列-動態規劃(3):找零錢、走方格問題](https://mp.weixin.qq.com/s/fKqp5icO5CfwoUfZ5jQmeg) [算法系列-動態規劃(4):買賣股票的最佳時機](https://mp.weixin.qq.com/s/ubDMTzaSVw3Ks0F9YIXFeg) --- ### 新生韭菜羅拉 自從上次看到八哥收藏舊幣,羅拉也想給自己搗鼓個副業,賺點零花錢。 於是她瞄上了股票,作為股場新人,羅拉可是滿懷信心的。 覺得自己只要順應潮流,識大體,懂進退,不貪心,即使不賺大錢,也不至於虧錢。 所以她想拿個一千八百試試水。 八哥作為過來人,股票玩得稀碎,當年也是這麼過來的,狠狠的當了一波韭菜。 但是看羅拉的勁頭,不被收割一次是勸不住她的富婆夢的。 就看看她怎麼搗鼓吧。 羅拉這幾天一直盯著手機看股票行情。 時而欣喜,時而嘆氣。 看來時機差不多了,八哥準備落井...關心一下羅拉。 對話記錄 | ---|

**八哥**

羅拉,炒股也有幾天了,你的富婆夢是否近了一步?|

**羅拉**

哎,別提了,這幾天天天盯著價格,眼睛都花了。
我買股票好像就跟我做對一樣,在我手上狂跌,我一賣就漲

| | |

**八哥**

是不是有一種,這些股票專門割你韭菜的趕腳。
只要持有就跌,賣出後就漲。
全世界都盯著你的一千八百|

**羅拉**

對啊,這幾天我只關注一支股票,也一直在買賣這個。
雖然不至於像你說的這麼誇張,但是確實現在小虧吧。
要麼因為下跌急急忙忙賣了,但是我一賣它馬上又漲回來了
要麼因為上漲態勢好,我持有了,但是轉眼它又跌了
總之,時機把握的不好

| | |

**八哥**

這麼看來你的富婆夢不怎麼順利呀|

**羅拉**

確實,果然小丑是我自己嗎?
也對,要是這麼容易,誰還老老實實幹活啊,都去炒股的了| | |

**八哥**

是的
所以我一開始也不勸你,畢竟大家開始的心態和你都差不多。
只有被割過韭菜,才會知道炒股高風險,高回報。不是一般人能玩的。|

**羅拉**

哎,白白浪費了幾天時間,
看來我還是適合去玩雞,找個靠譜雞放著都比這個強| | |

**八哥**

富婆夢可能毫無收穫,但是這個經歷到時可以用來提升一下自己,
買賣股票可是一個很經典的演算法題哦。
當然這個事後諸葛亮的題目。|

**羅拉**

演算法?有點意思,說來瞅瞅| | |

**八哥**

行,我把幾個經典的案例說一下吧| 說到炒股, 想當年八哥的神操作...,淚流滿面 ![八哥韭菜曲線](https://img2020.cnblogs.com/blog/1293390/202012/1293390-20201231224506039-496227391.png) -------------- ### 買賣股票的最佳時機(交易一次) “先來第一個題目,” “羅拉,你把你最近的股票七八天的股價給我列一下” “好,最近七八天的價格是這樣的:``{9,11,7,5,7,10,18,3}`` “嗯,我們現在先來最簡單的,題目如下:” >給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。 >如果你最多隻允許完成一筆交易(即買入和賣出一支股票一次), >設計一個演算法來計算你所能獲取的最大利潤。 >注意:你不能在買入股票前賣出股票。 “你試著分析看看。” “行我試試” “要想一次交易收益最大”, “那麼必須保證我是在最低點買入最高點賣出。這個樣就可以保證我的收益最大”。 “在這裡我們的最低點是``3``,最高點是``18``,這樣算的話最大收益是``15``”。 “嗯,不對,``3``是在``18``後面,這樣不符合邏輯”。 “應該是要保證最低價格在最高價格前面,要先買了才能買”。 “所以,假設今天是第``i``天,我只要記錄``i-1``天之前的最低價格”, “用今天的價格減去最低價格得到利潤,然後選取最大的利潤即可”。 “嗯,典型動態規劃特徵”。 “我用``dp[i]``記錄前``i-1``天的最低價格,” “邊界值為第``0``天股價設定為最大,保證``dp[1]``以後最小值”。 “哈哈,姐姐明白了”。 羅拉自通道,然後開始編碼。 ``` public class StockTrading { public static void main(String[] args) { int[] prices = {9, 11, 7, 5, 7, 10, 18, 3}; int[] pricesUp = {2,3,4,5,6,7,8,9}; int[] pricesDown = {9,8,7,6,5,4,3,2}; System.out.println("prices 一次交易最大利潤為: "+stockTrading1(prices)); System.out.println("pricesUp 一次交易最大利潤為: "+stockTrading1(pricesUp)); System.out.println("pricesDown 一次交易最大利潤為: "+stockTrading1(pricesDown)); } public static int stockTrading1(int[] prices) { if(prices==null || prices.length<2) return 0; int[] dp = new int[prices.length + 1]; //設定邊界 dp[0] = Integer.MAX_VALUE;//為了後面能取到最小值,dp[0]設定為Integer.MAX_VALUE int max = Integer.MIN_VALUE;//一開始利潤為Integer.MIN_VALUE for (int i = 1; i <= prices.length; i++) { max = Math.max(max,prices[i-1] - dp[i-1]); dp[i] = Math.min(dp[i - 1], prices[i-1]); } return max>=0?max:0;//利潤不能為負數,畢竟沒有傻子 } } ``` ``` //輸出結果 prices 一次交易最大利潤為: 13 pricesUp 一次交易最大利潤為: 7 pricesDown 一次交易最大利潤為: 0 ``` “不錯,結果也沒錯,可是你沒必要萬事皆動態吧”。 “吹毛求疵,如果我要用``O(1)``的時間複雜度,咋整?” 八哥有氣無力道。 “明明有簡單點、更高效的寫法,比如這樣: ” ``` public class StockTrading { public static void main(String[] args) { int[] prices = {9, 11, 7, 5, 7, 10, 18, 3}; int[] pricesUp = {2, 3, 4, 5, 6, 7, 8, 9}; int[] pricesDown = {9, 8, 7, 6, 5, 4, 3, 2}; System.out.println("prices 一次交易最大利潤為: " + stockTrading1(prices)); System.out.println("pricesUp 一次交易最大利潤為: " + stockTrading1(pricesUp)); System.out.println("pricesDown 一次交易最大利潤為: " + stockTrading1(pricesDown)); } public static int stockTrading1(int[] prices) { if (prices == null || prices.length < 2) return 0; //設定邊界 int min = Math.min(prices[0], prices[1]);//記錄前兩天的最低價格 int max = prices[1] - prices[0];//記錄前兩天利潤 for (int i = 2; i < prices.length; i++) { max = Math.max(max, prices[i] - min); min = Math.min(min, prices[i]); } return max >= 0 ? max : 0; //利潤不能為負數,畢竟沒有傻子 } } ``` ``` //輸出結果 prices 一次交易最大利潤為: 13 pricesUp 一次交易最大利潤為: 7 pricesDown 一次交易最大利潤為: 0 ``` “不過這個一般問題不大,我只是想說你不要陷入一個誤區就是啥都鑽到動態裡面去”。 “而忽視其他的方法”。 “哦,自從學了動態,好像確實有點凡事只想動態了,不過你這個本質還是動態吧”,羅拉尷尬道。 “嗯,這麼說也沒錯,就是壓縮一下空間而已,能想到動態不是壞事,只要不鑽牛角尖就好了”。 “這是最簡單的,接下來我們看看下一個問題”。 ---- ### 買賣股票的最佳時機(交易多次) “第二個問題如下:” >給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。 >設計一個演算法來計算你所能獲取的最大利潤。 >你可以儘可能地完成更多的交易(多次買賣一支股票)。 >注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。 “這是股票問題的第二類,你看看這個要怎麼處理”。 “嗯,我先看看”, “如果我要算最大的,利潤,肯定是得逢低買入,逢高賣出”。 “不過有個限制條件,每天只能進行一次交易,只能買賣二選一”。 “我可以比較兩天的價格,如果第``i``天的價格``prices[i]``大於第``i-1``天的價格``prices[i-1]``,那麼我就在第``i-1``天買入,第``i``天賣出”。 “但是這會存在一個問題,如果我連續兩天都是上漲的,這樣算會出問題”。 “比如``pri