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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

算法-贪心/动态规划-买卖股票的最佳时机

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

1 買賣股票的最佳時機 V1

1.1 概述

1.1.1 題目出處

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/

1.1.2 題目描述

給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。

如果你最多只允許完成一筆交易(即買入和賣出一支股票一次),設計一個算法來計算你所能獲取的最大利潤。

注意:你不能在買入股票前賣出股票。

示例 1:

輸入: [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。

1.2 雙指針

1.2.1 思路

雙指針,一個記錄最小股票價格,一個記錄結果。

1.2.2 代碼

class Solution {public int maxProfit(int[] prices) {int left = Integer.MAX_VALUE;int result = 0;for(int p : prices){if(p < left){// 小于左邊界,則更新左邊界left = p;} else if(p - left > result){// 利潤大于已記錄的最大利潤result = Math.max(result, p - left);}}return result;} }

1.2.3 時間復雜度


O(N)

1.2.4 空間復雜度

O(1)

1.3 動態規劃 V1

1.3.1 思路

dp[i]表示前i日的最大利潤。

則轉移方程為:
dp[i] = max( dp[i-1], prices[i] - min(prices[0 -> i]))。

也就是說,每日的最大利潤為以下兩項的大者:

  • 昨天的做大利潤
  • 在當天以前的某個股價最低的一天買入股票,并在當天賣出股票,以此賺取的利潤

初始:
dp[0] = 0;

返回值:
dp[prices.length - 1]

1.3.2 代碼

class Solution {public int maxProfit(int[] prices) {if(prices == null || prices.length < 2){return 0;}int[] dp = new int[prices.length];dp[0] = 0;// 記錄當天以前股價最低的一天的價格int min = prices[0];for(int i = 1; i < prices.length ; i++){dp[i] = Math.max(dp[i - 1], prices[i] - min);min = Math.min(min, prices[i]);}return dp[prices.length - 1];} }

1.3.3 時間復雜度


O(N)

1.3.4 空間復雜度

O(N)

1.4 動態規劃 V2

1.4.1 思路

其實我們只需要記錄當前最大利潤,不需要記錄每一天的最大利潤。

因為每天的最大利潤肯定是不會比前面的小的。

所以只需要一個變量存最大利潤即可,不需要dp[n]。

1.4.2 代碼

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

1.4.3 時間復雜度


O(N)

1.4.4 空間復雜度

O(1)

2 買賣股票的最佳時機 V2

2.1 概述

2.1.1 題目出處

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/

2.1.2 題目描述

給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。

設計一個算法來計算你所能獲取的最大利潤。你可以盡可能地完成更多的交易(多次買賣一支股票)。

注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

示例 1:

輸入: [7,1,5,3,6,4]
輸出: 7
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 3 天(股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。
隨后,在第 4 天(股票價格 = 3)的時候買入,在第 5 天(股票價格 = 6)的時候賣出, 這筆交易所能獲得利潤 = 6-3 = 3 。
示例 2:

輸入: [1,2,3,4,5]
輸出: 4
解釋: 在第 1 天(股票價格 = 1)的時候買入,在第 5 天 (股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接連購買股票,之后再將它們賣出。
因為這樣屬于同時參與了多筆交易,你必須在再次購買前出售掉之前的股票。
示例 3:

輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。

提示:

1 <= prices.length <= 3 * 10 ^ 4 0 <= prices[i] <= 10 ^ 4

2.2 貪心

2.2.1 思路

只要后項大于前項,則累加到結果中。
想象連續兩次漲,就累加了兩次的差值,其實就等于 第三次賣 - 第一次買的差值

其實是一個trick,沒有實際操作性,因為你不可能提前知道連續兩天的價格來決定第一天低價格買入第二天高價格賣出。

以下內容轉自貪心算法詳解,作者 liweiwei1419

因此,

  • “貪心算法” 和 “動態規劃”、“回溯搜索” 算法一樣,完成一件事情,是分步決策的;
  • “貪心算法” 在每一步總是做出在當前看來最好的選擇,“最好” 的意思往往根據題目而來,可能是 “最小”,也可能是 “最大”;
  • 貪心算法和動態規劃相比,它既不看前面(也就是說它不需要從前面的狀態轉移過來),也不看后面(無后效性,后面的選擇不會對前面的選擇有影響)
    因此貪心算法時間復雜度一般是線性的,空間復雜度是常數級別的。

這道題 “貪心” 的地方在于,對于 “今天的股價 - 昨天的股價”,得到的結果有 3 種可能:

  • 正數
  • 0
  • 負數。

貪心算法的決策是:只累加正數。

2.2.2 代碼

class Solution {public int maxProfit(int[] prices) {int result = 0;if(prices.length < 2){return result;}// 只要后項大于前項,則累加到結果中。// 想象連續兩次漲,就累加了兩次的差值,其實就等于 第三次賣 - 第一次買的差值for(int i = 1; i < prices.length; i ++){if(prices[i] > prices[i-1]){result += (prices[i] - prices[i-1]);}}return result;} }

2.2.3 時間復雜度


O(N)

2.2.4 空間復雜度

O(1)

2.3 動態規劃 V1

2.3.1 思路

可用貪心解決的一般也能用動態規劃。

  • dp[i][0]代表該天不持有股票的最大利潤
  • dp[i][1] 代表該天持有股票的最大利潤

則轉移方程為:

dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i]); dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i]);

初始:

dp[0][0] = 0; dp[0][1] = 0 - prices[0];

返回值:

// 因為最后一天必須不能持有股票,否則肯定利潤小于dp[prices.length - 1][0] dp[prices.length - 1][0]

2.3.2 代碼

class Solution {public int maxProfit(int[] prices) {if(prices.length < 2){return 0;}// dp[i][0]代表該天不持有股票的最大利潤// dp[i][1] 代表該天持有股票的最大利潤int[][] dp = new int[prices.length][2];dp[0][0] = 0;dp[0][1] = 0 - prices[0];for(int i = 1; i < prices.length; i ++){// 第二個表示 i-1日持有股票,但在i日賣出// 因為這暗含了加上 用 prices[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], dp[i-1][0] - prices[i]);}return dp[prices.length - 1][0];} }

2.3.3 時間復雜度


O(N)

2.3.4 空間復雜度

O(N)

2.4 動態規劃 V2

2.4.1 思路

最大利潤不會減少,所以也可以用兩個變量分別記錄擁有和不擁有股票時的最大利潤。

這樣空間復雜度降為O(N)

2.3.2 代碼

class Solution {public int maxProfit(int[] prices) {if(prices.length < 2){return 0;}int haveCost = 0 - prices[0];int notHaveCost = 0;for(int i = 1; i < prices.length; i ++){// 第二個表示 i-1日持有股票,但在i日賣出// 因為這暗含了加上 用 prices[i] 減去 前面買的那只股票的價格的差價notHaveCost = Math.max(notHaveCost, haveCost + prices[i]);haveCost = Math.max(haveCost, notHaveCost - prices[i]);}return notHaveCost;} }

2.4.3 時間復雜度


O(N)

2.4.4 空間復雜度

O(1)

3 最佳買賣股票時機含冷凍期

3.1 概述

3.1.1 題目出處

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/

3.1.2 題目描述

給定一個整數數組,其中第 i 個元素代表了第 i 天的股票價格 。?

設計一個算法計算出最大利潤。在滿足以下約束條件下,你可以盡可能地完成更多的交易(多次買賣一支股票):

你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
賣出股票后,你無法在第二天買入股票 (即冷凍期為 1 天)。
示例:

輸入: [1,2,3,0,2]
輸出: 3
解釋: 對應的交易狀態為: [買入, 賣出, 冷凍期, 買入, 賣出]

3.3 動態規劃 V1

3.3.1 思路

考慮四種狀態的轉移圖如下:

  • dp[i][0]代表該天不持有股票且非賣出的最大利潤
    dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2]);
  • dp[i][1] 代表該天賣出股票的最大利潤
    dp[i][1] = dp[i - 1][3] + prices[i];
  • dp[i][2] 代表該天位冷凍期最大利潤
    dp[i][2] = dp[i - 1][1];
  • dp[i][3] 代表該天賣出股票的最大利潤
    dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][0] - prices[i], dp[i-1][2] - prices[i]);

初始:

// 不持股,非賣出 dp[0][0] = 0; // 賣出 dp[0][1] = 0; // 冷凍 dp[0][2] = 0; // 持股 dp[0][3] = 0 - prices[0];

返回值:
最后結果是不持股的三種狀態的最大值

3.3.2 代碼

class Solution {public int maxProfit(int[] prices) {if(prices == null || prices.length < 2){return 0;}int[][] dp = new int[prices.length][4]; // 不持股,非賣出dp[0][0] = 0;// 賣出dp[0][1] = 0;// 冷凍dp[0][2] = 0;// 持股dp[0][3] = 0 - prices[0];for(int i = 1; i < prices.length; i++){// i日為不持股非賣出,則肯定是由i-1天不持股非賣出或冷凍轉移而來dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2]);// i日為賣出,則肯定是由i-1天持股且在i日賣出dp[i][1] = dp[i - 1][3] + prices[i];// i日為冷凍,則肯定是由i-1天賣出狀態轉移而來dp[i][2] = dp[i - 1][1];// i日為持股,則i-1天持股或(冷凍 || 不持股非賣出)且在i日買入持股dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][0] - prices[i]);dp[i][3] = Math.max(dp[i][3], dp[i-1][2] - prices[i]);}// 最后結果是不持股的三種狀態的最大值int result = Math.max(dp[prices.length - 1][0], dp[prices.length - 1][1]);result = Math.max(result, dp[prices.length - 1][2]);return result;} }

3.3.3 時間復雜度


O(N)

3.3.4 空間復雜度

O(N)

3.4 動態規劃 V2

3.4.1 思路

這道題目最大利潤也是遞增,而且是只需要考慮i日和i-1日的關系,所以也已用4個變量分別記錄i日的四種狀態時的最大利潤。

3.4.2 代碼

//TODO

3.4.3 時間復雜度

O(N)

3.4.4 空間復雜度

O(1)

4 買賣股票的最佳時機 III

4.1 概述

4.1.1 題目出處

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/

4.1.2 題目描述

4.2 動態規劃

4.2.1 思路

推導過程詳見詳細通俗的思路分析,多解法

4.2.2 代碼

class Solution {public int maxProfit(int k, int[] prices) {if (prices.length == 0) {return 0;}if (k > prices.length / 2) {// 此時,即變為可以任意交易次數,因為當天只能買或賣,直接使用貪心法求解即可return maxProfitGreedy(prices);}int K = k;int[] dp = new int[K + 1];int min[] = new int[K + 1];for (int i = 1; i <= K; i++) {min[i] = prices[0];}for (int i = 1; i < prices.length; i++) {for (int j = 1; j <= K; j++) {min[j] = Math.min(prices[i] - dp[j - 1], min[j]);dp[j] = Math.max(dp[j], prices[i] - min[j]);}}return dp[K];}public int maxProfitGreedy(int[] prices) {int result = 0;if(prices.length < 2){return result;}// 只要后項大于前項,則累加到結果中。// 想象連續兩次漲,就累加了兩次的差值,其實就等于 第三次賣 - 第一次買的差值for(int i = 1; i < prices.length; i ++){if(prices[i] > prices[i-1]){result += (prices[i] - prices[i-1]);}}return result;}

4.3 狀態機

4.3.1 思路

遍歷每個股價日,需要考慮的狀態有四個:

  • 第一次買入后當前剩余的錢
  • 第一次賣出,以當前價格的股票賣出
  • 第二次買入后當前剩余的錢
  • 第二次賣出股票。

考慮四種狀態的轉移圖如下:

思路詳見 https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/discuss/149383/Easy-DP-solution-using-state-machine-O(n)-time-complexity-O(1)-space-complexity

這里最關鍵的一點,為什么能遍歷每天時都可以用這個轉移圖去判斷,因為對某一天來說,立刻買入又賣出的情況是無意義的,所以可以這樣轉移。也就是說,如果該天應該買入,則只會改變s1/s3,如果當天值得賣出,只會改變s2/s4。這是最重要的一點,如果想不明白的話可以把例子數值帶入,畫出每一次的幾個狀態的值就明白了。

返回值:Math.max(s4, 0) 因為s4可能為min,即只有一個股票日時,此時就不買就行了。

4.3.2 代碼

class Solution {public int maxProfit(int[] prices) {if(prices == null || prices.length == 0) {return 0;}// 進行初始化,第一天 s1 將股票買入,其他狀態全部初始化為最小值int s1=-prices[0], s2=Integer.MIN_VALUE, s3=Integer.MIN_VALUE, s4=Integer.MIN_VALUE;for(int i = 1; i < prices.length; i++) { s1 = Math.max(s1, 0 - prices[i]);s2 = Math.max(s2, s1 + prices[i]);s3 = Math.max(s3, s2 - prices[i]);s4 = Math.max(s4, s3 + prices[i]);}// 可能只有一個股票日,此時不買就是最佳return Math.max(0, s4);} }

4.3.3 時間復雜度

O(N)

4.3.4 空間復雜度

O(N)

參考文檔

  • 貪心算法詳解
  • 詳細通俗的思路分析,多解法

總結

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

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