1. 程式人生 > 其它 >劍指offer.63題 股票的最大利潤 經典dp問題求解與優化

劍指offer.63題 股票的最大利潤 經典dp問題求解與優化

技術標籤:Leetcode動態規劃leetcode演算法java

文章目錄


一、題目描述

本文所講解的是劍指offer第63題——股票的最大利潤,本題是一個經典的dp動態規劃問題。

示例 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。

二、貪心法

思路:新建一個與原來prices長度一致的陣列Matrix,用於記錄該天買入股票能夠獲得的最大收益。然後依次遍歷當天之後最高的價格,並將該價格保存於陣列Matrix中。

class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length==0)
            return 0;
        int Matrix[]=new int[prices.length];
        int max;
        int length=Matrix.length;
        for
(int i=length-2;i>=0;i--) { max=findMax(prices,i+1,length); Matrix[i]=(prices[i]>=prices[max])?0:(prices[max]-prices[i]); } max=0; for(int i=0;i<length;i++) { if(Matrix[i]>Matrix[max]) max=i; }
return Matrix[max]; } public int findMax(int []prices,int start,int end) { int max=start; for(int i=start+1;i<end;i++) { if(prices[i]>prices[max]) max=i; } return max; } }

三、動態規劃方法

  • 狀態定義: 設動態規劃列表 dp,dp[i]代表以 prices[i] 為結尾的子陣列的最大利潤(以下簡稱為 前 i日的最大利潤 )。

  • 轉移方程: 由於題目限定 “買賣該股票一次” ,因此前 i 日最大利潤 dp[i] 等於前 i - 1 日最大利潤 dp[i-1] 和第 i 日賣出的最大利潤中的最大值。

    前i日最大利潤=max(前(i−1)日最大利潤,第i日價格−前i日最低價格)
    

即:

	dp[i] = max(dp[i - 1], prices[i] - min(prices[0:i]))
  • 初始狀態: dp[0] = 0,即首日利潤為 0 ;
  • 返回值: dp[n - 1],其中 n 為 dp 列表長度。

程式碼如下所示:

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

其中,min用於表示當前迴圈到的日期之前的最低價格。
演算法對比
從上圖的提交記錄也可以看出,採用動態規劃後,演算法執行時間有了質的提升。

四、狀態轉移方程的優化

效率優化,以下為Leetcode官方解法中的概述:
時間複雜度降低: 前 i 日的最低價格min(prices[0:i]) 時間複雜度為 O(i)。而在遍歷 prices 時,可以藉助一個變數(記為成本 cost )每日更新最低價格。優化後的轉移方程為:

	dp[i]=max(dp[i−1],prices[i]−min(cost,prices[i]))

空間複雜度降低: 由於 dp[i] 只與 dp[i - 1], prices[i] , cost 相關,因此可使用一個變數(記為利潤 profit)代替 dp 列表。優化後的轉移方程為:

	profit=max(profit,prices[i]−min(cost,prices[i]))

複雜度分析:
時間複雜度 O(N): 其中 N 為 prices 列表長度,動態規劃需遍歷 pricesprices 。
空間複雜度 O(1) : 變數 cost和 profit使用常數大小的額外空間。

class Solution {
    public int maxProfit(int[] prices) {
     
        int cost = Integer.MAX_VALUE, profit = 0;
        for(int price : prices) {
            cost = Math.min(cost, price);
            profit = Math.max(profit, price - cost);
        }
        return profit;   
    }
}