lintcode:買賣股票的最佳時機 I


買賣股票的最佳時機

假設有一個數組,它的第i個元素是一支給定的股票在第i天的價格。如果你最多只允許完成一次交易(例如,一次買賣股票),設計一個算法來找出最大利潤。

樣例

給出一個數組樣例 [3,2,3,1,2], 返回 1 

解題

法一:直接暴力,時間發雜度O(N2)

public class Solution {
    /**
     * @param prices: Given an integer array
     * @return: Maximum profit
     */
    public int maxProfit(int[] prices) {
        // write your code here
        int Max = 0;
        if( prices == null || prices.length == 0)
            return 0;

        for(int i = 0;i< prices.length ;i++){
            for(int j = i;j< prices.length ;j++)
                Max = Math.max(Max, prices[j] - prices[i]);
        }
        return Max;
    }
}

 

法二:動態規划,選取最小的賣,最大的買,利潤最大。

public class Solution {
    /**
     * @param prices: Given an integer array
     * @return: Maximum profit
     */
    public int maxProfit(int[] prices) {
        // write your code here
        int result = 0;
        if( prices == null || prices.length == 0)
            return 0;
        int minbuy = prices[0];        
        for(int i = 1;i< prices.length ;i++){
            // 最小的購買,最大的賣
            result = Math.max(result,prices[i] - minbuy);
            minbuy = Math.min(minbuy,prices[i]);
        }
        return result;
    }
}

時間復雜度O(N)

Python

class Solution:
    """
    @param prices: Given an integer array
    @return: Maximum profit
    """
    def maxProfit(self, prices):
        # write your code here
        if prices == None or len(prices) ==0:
            return 0
        Min = prices[0]
        res = 0
        for p in prices:
            res = max(res,p-Min)
            Min = min(Min,p)
        return res 

 法三:分治法

參考《算法導論》

題目要求的是一次購買,一次賣出使得所獲價格變化最大。可以考慮每一天的價格變化,第i天的價格變化 等於第i天的價格減去i-1天的價格,這樣就會有許多價格變化的數據形成的數組,求這個數組的連續子數組的和的最大值就是答案了。為什么這個這個最大連續子數組就是答案?

假設原始數組是:,若最大收益是 an - a1

相鄰差數組是:,顯然這個的連續和也是an - a1

問題轉化為求最大連續子數組

對上圖的例子:

 

分治法求解最大子數組

假定我們要尋找子數組A[low,...,high]的最大子數組。使用分治法意味着我們要將子數組分成兩個規模盡量相同的子數組。也就是說,找到子數組的中央位置,比如:mid,然后考慮求兩個子數組A[low,...,mid] 和A[mid+1,...,high]。

A[low,...,high]的然后連續子數組A[i,...,j]所處的位置必然是一下三種情況之一:

(1)完全位於子數組A[low,...,mid]中,因此low<=i<=j<=mid

(2)完全位於子數組A[mid+1,...,high]中,因此mid+1<=i<=j<=high

(3)跨越了中間點,因此low<=i<=mid<=j<=high

所以,可以遞歸的求解(1)(2)兩種情況的最大子數組,剩下的就是對(3)情況尋找跨越中間點的最大子數組,然后在三種情況中選取和最大者。如下圖所示

 

對於跨越中間點的最大子數組,可以在線性時間內求解。可以找出A[i,...,mid] 和A[mid+1,...,j]的最大子數組,合並就是答案。

參考算法導論寫的尋找經過中間點時候的最大連續子數組

    public int findMaxCrossingSubarray(int[] A,int low,int mid,int high){
        if(low > mid || mid>high)
            return Integer.MIN_VALUE;
        int leftSum = Integer.MIN_VALUE;
        int rightSum = Integer.MIN_VALUE;
        int sum = 0;
        int maxleft = -1;
        int maxright = -1;
        for(int i = mid;i>=low;i--){
            sum+=A[i];
            if( sum >= leftSum){// 向左只要和增加就更新
                leftSum = sum;
                maxleft = i;
            }
        }
        sum = 0;
        for(int j = mid+1;j<=high;j++){
            sum+=A[j];
            if(sum>=rightSum){
                rightSum = sum;
                maxright = j;
            }
        }
        return leftSum + rightSum;
    }

算法導論上的偽代碼

時間復雜度O(N)

上面有返回的邊界,我只是返回了子數組的最大值

下面在遞歸的求解整個數組的最大連續子數組

    public int findMaxSubarray(int[] A,int low,int high){
        if(low == high)
            return Math.max(A[low],0);
        else{
            int mid = low + (high - low)/2;// 防止越界
            int leftSum = findMaxSubarray(A,low,mid);//(1)
            int rightSum = findMaxSubarray(A,mid+1,high);//(2)
            int midSum = findMaxCrossingSubarray(A,low,mid,high);//(3)
            int sum = Math.max(leftSum,rightSum);
            sum = Math.max(sum,midSum);
            sum = Math.max(sum,0);
            return sum;
        }
    }

上面標的(1)( 2)( 3)對應上面分析的(1)(2)(3)

上面代碼中最后的結果和0求了最大值,lintcode測試用例可以不買不賣的情況,由於買了一定會虧,就不買了的情況,題目要求最大一次交易,就是可以不交易的了。

算法導論上的偽代碼

時間復雜度分析:

遞歸情況:

 這個等式很顯然的

當n=1的時候就是O(1)

所以:

時間復雜度是:

具體時間復雜度求解參考《算法導論》

對於求解最大子數組,當然也可以運用動態規划求解

全部程序

public class Solution {
    /**
     * @param prices: Given an integer array
     * @return: Maximum profit
     */
    public int maxProfit(int[] prices) {
        // write your code here
        if(prices == null || prices.length == 0)
            return 0;
        int[] A = new int[prices.length - 1];
        for(int i = 1;i<prices.length ;i++)
            A[i-1] = prices[i] - prices[i-1];
        int maxSubarray = findMaxSubarray(A,0,A.length - 1);
        return maxSubarray;
    }
    public int findMaxSubarray(int[] A,int low,int high){
        if(low == high)
            return Math.max(A[low],0);
        else{
            int mid = low + (high - low)/2;// 防止越界
            int leftSum = findMaxSubarray(A,low,mid);//(1)
            int rightSum = findMaxSubarray(A,mid+1,high);//(2)
            int midSum = findMaxCrossingSubarray(A,low,mid,high);//(3)
            int sum = Math.max(leftSum,rightSum);
            sum = Math.max(sum,midSum);
            sum = Math.max(sum,0);
            return sum;
        }
    }
    public int findMaxCrossingSubarray(int[] A,int low,int mid,int high){
        if(low > mid || mid>high)
            return Integer.MIN_VALUE;
        int leftSum = Integer.MIN_VALUE;
        int rightSum = Integer.MIN_VALUE;
        int sum = 0;
        int maxleft = -1;
        int maxright = -1;
        for(int i = mid;i>=low;i--){
            sum+=A[i];
            if( sum >= leftSum){// 向左只要和增加就更新
                leftSum = sum;
                maxleft = i;
            }
        }
        sum = 0;
        for(int j = mid+1;j<=high;j++){
            sum+=A[j];
            if(sum>=rightSum){
                rightSum = sum;
                maxright = j;
            }
        }
        return leftSum + rightSum;
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM