天天看點

Leetcode:121.買賣股票的最佳時機&&122.買賣股票的最佳時機II&&123.買賣股票的最佳時機III

121.買賣股票的最佳時機

給定一個數組,它的第 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。      

122.買賣股票的最佳時機II

給定一個數組,它的第 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。      

123.買賣股票的最佳時機III

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

設計一個算法來計算你所能擷取的最大利潤。你最多可以完成 兩筆 交易。

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

示例 1:

輸入: [3,3,5,0,0,3,1,4]
輸出: 6
解釋: 在第 4 天(股票價格 = 0)的時候買入,在第 6 天(股票價格 = 3)的時候賣出,這筆交易所能獲得利潤 = 3-0 = 3 。
     随後,在第 7 天(股票價格 = 1)的時候買入,在第 8 天 (股票價格 = 4)的時候賣出,這筆交易所能獲得利潤 = 4-1 = 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。      

解題思路:

這三題都是類似的思想,第一題的掌握程度直接決定,二,三題是否能較快得解出來。

121中,有兩種解法。

121-1:求解第i天買入股票,價格為val[i],能賺取的最大利潤m[i]。[i,end)區間的最大值為max,于是m[i]=max-val[i]。最終再求取max(m[i])即為最大利潤,由于每次計算都隻需目前值以及之後的最大值,是以采用從後往前的周遊方法,一次掃描數組即可達到要求。

121-2:求解第i天賣出股票,價格為val[i],能賺取的最大利潤m[i]。[0,i]區間的最小值為min,于是m[i]=val[i]-min。最終在求取max(m[i])即為最大利潤,由于每次計算都隻需要目前值以及之前的最小值,是以采用從前往後的周遊方法,一次掃描數組也可以達到要求。

122中,可以多次購買,這就意味着能賺則賺。每次找一個極小點,然後再找最近的一個極大值,擷取一次利潤,直到數組末尾。

123實際上是121的加強版。最多兩次購買,買賣的區間不能重複,這就意味着必然有一天賣出了股票,然後再接下來的日子裡又買了一個。本題通過巧妙的設計,就能将時間複雜度降低到O(n)。具體步驟如下:

1. 用121-2的方法計算出每天賣出股票賺取的最大利潤data1;——>O(n)

2. 用121-1的方法計算出每天買入股票賺取的最大利潤data2;——>O(n)

3. 計算data2中,第i天之後的最大值,從後往前掃描,得到data3;——>O(n)

4.計算 第i天賣出的最大利潤+第i天之後買入的最大利潤=data1[i]+data3[i+1]的最大值res;——>O(n)

5. 最終傳回res即為最大值,特殊地,當i=最後一天時,data1+data3得到的是隻購買一次的值,是以無需再次考慮隻購買一次的結果。

最終算法的時間複雜度為O(n)

C++代碼121-122-123

class Solution {

public:

    int maxProfit(vector<int>& prices) {

        int size = prices.size();

        if (size == 0) return 0;

        int max = 0, max_price = prices[size - 1];

        for (int i = size; i >= 1; i--) {

            if (prices[i - 1] > max_price) max_price = prices[i - 1];

            else {

                max = (max_price - prices[i - 1] > max ? max_price - prices[i - 1] : max);

            }

        }

        return max;

    }

};

class Solution {

public:

    int maxProfit(vector<int>& prices) {

        int size = prices.size();

        if (size <= 1) return 0;

        if (size == 2) { return (prices[0] < prices[1] ? prices[1] - prices[0] : 0); }

        int max = -1, min = -1, val = 0;

        for (int i = 1; i <= size; i++) {

            if (i == 1) {

                if (prices[i - 1] < prices[i]) {

                    min = prices[i-1];

                }

                continue;

            } 

            if (i == size) {

                if (prices[i - 1] > prices[i - 2]){

                    max = prices[i - 1]; 

                    if (min >= 0) {

                        val += (max - min);

                    }

                    max = -1; min = -1;

                 }

                continue;

            }

            if (prices[i - 1] <= prices[i - 2] && prices[i - 1] <= prices[i]) { 

                min = prices[i - 1]; 

            }

            if (prices[i - 1] >= prices[i - 2] && prices[i - 1] >= prices[i]) { 

                max = prices[i - 1]; 

                if (min >= 0) {

                    val += (max - min);

                }

                max = -1; min = -1;

            }

        }

        return val;

    }

};

class Solution {

public:

    int maxProfit(vector<int>& prices) {

        int size = prices.size();

        if (size <= 1) return 0;

        //記錄每一天購買能夠擷取的最大利潤

        vector<int> post2pre(size, 0), pre2post(size, 0);

        int _max = prices[size - 1], i, _min = prices[0];

        for (i = size; i >= 1; i--) {

            _max = (prices[i - 1] > _max ? prices[i - 1] : _max);

            post2pre[i - 1] = _max - prices[i - 1];

        }

        //記錄每一天賣出能夠擷取的最大利潤

        for (i = 1; i <= size; i++) {

            _min = (prices[i - 1] < _min ? prices[i - 1] : _min);

            pre2post[i - 1] = prices[i - 1] - _min;

        }

        //記錄第i天之後購買入能夠擷取的最大利潤

        vector<int> post2pre_max(size, 0);

        post2pre_max[size - 1] = post2pre[size - 1];

        for (i = size - 1; i >= 1; i--) {

            post2pre_max[i - 1] = (post2pre[i - 1]>post2pre_max[i] ? post2pre[i - 1] : post2pre_max[i]);

        }

        //必然是在某天賣出,然後在當天之後的日子買入

        int res = post2pre_max[0];//隻買一次的最大利潤

        for (i = 1; i <= size-1; i++) {

            res = (res > (post2pre_max[i] + pre2post[i - 1]) ? res : (post2pre_max[i] + pre2post[i - 1]));

        }

        return res;

    }

};

Leetcode:121.買賣股票的最佳時機&amp;&amp;122.買賣股票的最佳時機II&amp;&amp;123.買賣股票的最佳時機III
Leetcode:121.買賣股票的最佳時機&amp;&amp;122.買賣股票的最佳時機II&amp;&amp;123.買賣股票的最佳時機III
Leetcode:121.買賣股票的最佳時機&amp;&amp;122.買賣股票的最佳時機II&amp;&amp;123.買賣股票的最佳時機III