日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【算法-LeetCode】121. 买卖股票的最佳时机(动态规划;贪心)

發布時間:2023/12/20 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【算法-LeetCode】121. 买卖股票的最佳时机(动态规划;贪心) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

121. 買賣股票的最佳時機 - 力扣(LeetCode)

發布:2021年9月15日13:44:26

問題描述及示例

給定一個數組 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。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock
著作權歸領扣網絡所有。商業轉載請聯系官方授權,非商業轉載請注明出處。

提示:
1 <= prices.length <= 105
0 <= prices[i] <= 104

我的題解

更新:2021年10月15日17:26:39

又寫了一道股票問題的題解,本篇博客中的代碼沒有相關的注釋,而下面參考博客中有我的詳細解釋,可以作為參考:

參考:【算法-LeetCode】122. 買賣股票的最佳時機 II(動態規劃;貪心)_賴念安的博客-CSDN博客

【更新結束】

我的題解1(暴力解法—因超時而無法通過)

這個思路還是很直接的,就是遍歷 prices 數組,然后在遍歷過程中再進行內部遍歷,假設外層遍歷到了 i,那內層遍歷就是由 0 到 i-1。在遍歷過程中用 profit 記錄利潤的歷史最大值。由于有兩層嵌套的遍歷,這就導致程序的時間復雜度為O(n2)O(n^2)O(n2),當遇到比較大的數據量時,程序運行時間就會超出時間限制。

/*** @param {number[]} prices* @return {number}*/ var maxProfit = function(prices) {// profit用于動態存儲利潤最大值,遍歷完成后,其值就是我們想要的結果let profit = 0;// 因為不能再同一天買賣股票獲得利潤,所以當prices的長度為1時應當直接返回0。if(prices.length === 1) {return 0;}// 開始遍歷prices數組,sell是我們假設賣出股票的日期for(let sell = 1; sell < prices.length; sell++) {// buy是我們假設買入股票的日期,買入日一定是在賣出日之前的for(let buy = 0; buy < sell; buy++) {// 若假設的賣出日的股票價格不比買入日的股票價格高,// 那么說明這種買賣方案的利潤不為正,可以直接結束本次內層遍歷if(prices[sell] <= prices[buy]) {continue;}// 如果有利可圖,那么就計算當前買賣方案所能得到的利潤,// 與歷史最大利潤做比較,并把profit更新為較大的那個值profit = Math.max(profit, prices[sell] - prices[buy]);}}// 遍歷完成后,profit就是所能得到的最大利潤return profit; };提交記錄 201 / 211 個通過測試用例 狀態:超出時間限制 時間:2021/09/14 21:42


很可惜,這種解法確實是很好理解,但是碰到超長的測試用例是還是力不從心……

我的題解2(動態規劃—失敗)

有關動態規劃的解法,我之前寫了相關的總結步驟,可以參考下面的博客:

參考:【算法-LeetCode】53. 最大子序和(動態規劃初體驗)_賴念安的博客-CSDN博客

因為偷瞄到【提示】里有“動態規劃”的標簽,所以,我就開始往這個角度思考。寫出了下面這個看起來很不像動態規劃的動態規劃解法。

我這里的 dp 數組的含義是:dp[i] 表示第 i 天所能獲得的最大利潤。而指針 p 則用于記錄當前獲得最大利潤時的日期下標。

程序本質還是在尋找一段股價總體上升的區間的。

程序所匹配的目標股價區間
/*** @param {number[]} prices* @return {number}*/ var maxProfit = function (prices) {// 創建長度和prices數組相同的dp數組,并默認填充0,這在后面的判斷中會用上let dp = Array.from({ length: prices.length }).fill(0);// 初始化dpdp[0] = 0;// p指針用于標識當前獲得最大利潤時的那個賣出日let p = 0;for (let i = 1; i < prices.length; i++) {// 如果當前股價大于此前所能獲取最大利潤時的股價,則說明我現在賣出股票能獲取更多的利潤if (prices[i] > prices[p]) {// 如果當前賣出能夠獲取更多利潤,那么當前能夠獲得利潤就是在之前的最大利潤基礎上// 加上當前股價與此前獲得最大利潤時的股價的差價dp[i] = dp[p] + prices[i] - prices[p];// 更新p指針的指向,標識目前第i天賣出股票所能獲得的利潤最大p = i;// 如果已經完成了p指針的更新,那么后續的操作就不需要進行了,直接結束本次循環continue;}// 如果現在賣出不能獲得更大的利潤,則進入下一次循環,此前需要判斷p指針是否需要移動// 這里判斷dp[p]是否是初始狀態是為了讓應對還未遇到下降的股價時的情況if (dp[p] === 0) {p++;}}// 最后,在p指針所指向的日期賣出股票能獲得最大利潤,將其返回return dp[p]; };這次我沒有點擊提交,畢竟提交后也是不通過的

然而很不幸的是,雖然內置的兩個小測試用例能夠通過測試,但是那個超長測試用例還是沒有通過,這次不是超時,而是壓根就是邏輯錯誤。

兩個內置測試用例的運行結果(通過)

上面的那個超長用例的運行結果(未通過)

于是,我就開始思考為什么沒有通過……后來突然想明白了,我這種解法就是默認了 prices 中第一次遇到的最小值(也就是下圖中的A點)就是整個數組的最小值了(也即是我們心里預期的那個最佳的股票買入日的股價)。可事實上,也許后續還有可能出現比A點價格更低的股價(比如下面的點B)。由下面的圖可以看到,很顯然,在B點買入才能讓后期賣出股票時利潤最大。

我的題解3(動態規劃—成功)

嚴格來說,這個解法不是我想出來的,在此感謝分享題解的博主和題友。

下面這種動態規劃的解法主要還是參考以下兩位博主的思路:

參考:暴力解法、動態規劃(Java) - 買賣股票的最佳時機 - 力扣(LeetCode)
參考: 「代碼隨想錄」帶你學透股票系列!121. 買賣股票的最佳時機【暴力】【貪心】【動態規劃】詳解 - 買賣股票的最佳時機 - 力扣(LeetCode)

/*** @param {number[]} prices* @return {number}*/ var maxProfit = function (prices) {if(prices.length === 1) {return 0;}let dp = Array.from({length: prices.length}).map(() => Array.from({length: 2}));dp[0][0] = 0;dp[0][1] = -prices[0];for(let i = 1; i < prices.length; i++) {dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i]);dp[i][1] = Math.max(dp[i-1][1], -prices[i]);}return dp[dp.length-1][0]; };提交記錄 211 / 211 個通過測試用例 狀態:通過 執行用時:632ms,在所有JavaScript提交中擊敗了5.14%的用戶 內存消耗:70MB,在所有JavaScript提交中擊敗了5.01%的用戶 時間:2021/09/17 16:30

可以看到,這種解法只需要遍歷一次數組,沒有雙層嵌套的 for 循環。這是二維數組的解法,當然還是可以像我之前做的滾動數組那樣通過滾動數組的方式來降低空間消耗。這在上面提到的參考題解中也有提到,思路還是和之前提到的差不多,這里就不再敘述了, 畢竟滾動數組確實不好描述。

詳細的解釋在我上面提到的兩個參考題解中都有非常詳細的解釋,我這里就不再重復了。總的來說還是動態規劃的那幾個流程,我之所以沒有想到這種二維數組的解法,就是在第一步確定 dp 數組的含義時想歪了,本題中最關鍵的地方就在于此,只要明白了這個點,后面的推導就水到渠成了。不得不說雖然動態規劃的解題特征比較清晰,但是對其的應用確實千變萬化的……

當然,在【官方題解】中,也有一種看起來形式不太相同的動態規劃解法,其評論區里也有題友提出了一些比較好的理解思路,非常值得參考。

我的題解3(貪心)

在相關的題解中,也有一種利用貪心的解法:

/*** @param {number[]} prices* @return {number}*/ var maxProfit = function (prices) {if(prices.length === 1) {return 0;}let profit = 0;let minPrice = Infinity;for(let i = 0; i < prices.length; i++) {minPrice = Math.min(minPrice, prices[i]);profit = Math.max(profit, prices[i] - minPrice);}return profit; };提交記錄 211 / 211 個通過測試用例 狀態:通過 執行用時:96 ms, 在所有 JavaScript 提交中擊敗了67.27%的用戶 內存消耗:47.3 MB, 在所有 JavaScript 提交中擊敗了95.02%的用戶 時間:2021/09/17 16:53

總體思路就是在遍歷 prices 的過程中,用一個 minPrices 存儲所遇到的最低股價,用 profit 存儲目前能獲取的最大利潤,而且這個最大利潤就是遍歷過程中所獲取的最大股價差。其實這種方式我和我上面自己寫的那個失敗的動態規劃有點相似的味道。不過我的那個只能適應一小部分情況。

這段時間感覺做題的節奏沒能保持下去,主要還是一對亂七八糟的東西要處理,還有一些必要的技能要學,總的來說還是有點怠惰了。這篇題解記錄就不做詳細的逐句注釋了,因為主要還是看別人的題解,而且別人的題解也已經講得很清楚了。到時候回顧的時候應該也沒啥大問題吧。

官方題解

更新:2021年7月29日18:43:21

因為我考慮到著作權歸屬問題,所以【官方題解】部分我不再粘貼具體的代碼了,可到下方的鏈接中查看。

更新:2021年9月15日13:49:21

參考:買賣股票的最佳時機 - 買賣股票的最佳時機 - 力扣(LeetCode)

【更新結束】

有關參考

更新:2021年9月16日22:38:13
參考:暴力解法、動態規劃(Java) - 買賣股票的最佳時機 - 力扣(LeetCode)
參考: 「代碼隨想錄」帶你學透股票系列!121. 買賣股票的最佳時機【暴力】【貪心】【動態規劃】詳解 - 買賣股票的最佳時機 - 力扣(LeetCode)
更新:2021年9月17日17:30:57
參考:【算法-LeetCode】53. 最大子序和(動態規劃初體驗)_賴念安的博客-CSDN博客
更新:2021年10月15日17:26:39
參考:【算法-LeetCode】122. 買賣股票的最佳時機 II(動態規劃;貪心)_賴念安的博客-CSDN博客

后言

更新:2021年9月19日11:34:05

昨天睡覺前看了下自己的CSDN博客消息,發現有人給我這篇博客點贊,其中還有一位關注了我,我的粉絲數也從5變為了6,雖說談不上多么興奮,因為我的博客內容更多地是用于自己回顧的,沒想著能真正幫到其他人。但是總歸是自己的內容收到了其他人的肯定,還是非常感謝的~

昨晚收到的點贊消息

昨晚關注我的用戶

說實話,這篇博客我倒覺得寫得粗制濫造,因為之前我的題解博客中基本都是有逐行的解釋過程的,但是這篇博客卻沒有,尤其是關鍵的“動態規劃”和“貪心”算法,只是把從被人那里看到的內容大概理解之后再自己寫了一遍而已,代碼中也沒有相關的注釋,雖然要寫注釋也可以,但是我卻因為種種原因沒有寫(文章后面也有提到),其實主要原因還是我覺得這道題花費的周期有點長了,我有點不耐煩了,再加上還有其他事想忙,就導致沒有詳寫的熱情了。所以就想著水一水吧,反正自己回顧的時候能看得懂就好了。但是沒想到從前一兩天開始,這篇博客居然好像有越來越多的人閱讀,首先是有2人收藏(看到有人收藏的時候我自己還疑惑了一下),然后可能是因為收藏的緣故導致文章的權重變高,所以就使得有越來越多的人看到文章,閱讀量自然也就上升了(是突然上升的那種,剛開始看到的時候還是有點奇怪的),然后就是上面提到的昨晚發生的事了。然后早上起來想繼續寫博客,發現消息通知里有點贊、評論和私信,我想可能是我之前那篇閱讀量最高的文章有人點贊評論了吧:

參考:【win10 企業版 LTSC一鍵安裝微軟應用商店Microsoft Store】直接使用GitHub上的開源項目,不用自己敲命令(親測有效!!!),附卸載工具_賴念安的博客-CSDN博客_win10企業版ltsc安裝應用商店

因為這篇博客確實是解決了別人的痛點的,會受到點贊我也不意外。

但是點開消息后卻發現居然是本篇博客的點贊評論和私信,而且私信是CSDN官方發的消息,說是我的博客上了什么熱榜。

CSDN熱榜官方給我的私信

點擊去一看還真有!

我的這篇博客到了熱榜49名

后來一看文章的收藏量也直接從2變成了9,點贊數倒是保持為3,最奇怪的是,我的粉絲數,突然就從6變為了19(更新這里的時候好像變為21了)。好家伙,兩年的粉絲量都比不上這半天的……

想來估計是短時間內有人給我點贊收藏,提高了文章權重,然后上了熱榜,然后又有人看到,然后繼續提到權重……不得不感嘆流量的神奇。

講到這里,我覺得自己反而沒有什么特別的興奮之情了,就是覺得一篇感覺質量不如我之前的博客的文章受到了比往常更多的關注這件事讓我有點疑惑。同時也怕到后來自己寫博客的時候會考慮太多,反而忘了自己的目的,說實話,我還挺懷念自己五個小粉絲的時候的……(當然,大家關注我,我還是報以感激的~謝謝大家的支持!)希望我的其他自我感覺質量更好的博客也能給大家帶來幫助吧。

這篇博客的關鍵解題思路不是我獨立想出來的,所以更多地是在拾人牙慧,其實更希望大家去原作者那里點贊之類的(有關的參考博客我都在相關地方寫了)。

逼逼賴賴這么久,算是記錄一下自己對這篇博客的看法吧。總結下來就是:

我很疑惑:為啥自己覺得寫得不好的博客卻受到了小關注,但是自己覺得寫得好的文章反而沒有呢?不過,無論如何,如果博客能順便給別人帶來幫助,那真的很讓我開心!

另外,推薦一個這段時間看得比較多的LeetCode題解博主的個人題解網站,感覺質量還是很高的,非常感謝這位博主的分享!

參考:代碼隨想錄

當然,LeetCode題解區也有很多精彩的題解描述,可以對照思考。

【更新結束】

總結

以上是生活随笔為你收集整理的【算法-LeetCode】121. 买卖股票的最佳时机(动态规划;贪心)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。