1. 程式人生 > >領釦-121/122 最佳買賣時機 Best Time to Buy and Sell MD

領釦-121/122 最佳買賣時機 Best Time to Buy and Sell MD

目錄

Markdown版本筆記 我的GitHub首頁 我的部落格 我的微信 我的郵箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 [email protected]

領釦-121/122 最佳買賣時機 Best Time to Buy and Sell MD
***
目錄
===

買賣股票的最佳時機 -121

陣列 貪心演算法

題目

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

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

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

示例 1:

輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天的時候買入,在第 5 天的時候賣出,最大利潤 = 6-1 = 5 。
      注意利潤不能是 7-1 = 6, 因為賣出價格需要大於買入價格。

示例 2:

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

方法宣告:

class Solution {
    public int maxProfit(int[] prices) {
        
    }
}

暴力法

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

時間複雜度:O(n^2)
空間複雜度:O(1)

貪心演算法(波峰波谷法)

對於這道題,我們很明顯能感覺到,不是單純的找出陣列中的最小值和最大值,然後求他們的差的,因為最小值不一定在最大值的前面。

但是也很明顯,這道題確實是讓我們找最值的,問題出在哪呢?

問題出在,這道題其實是讓我們求極小值和極大值的,也即先找出一個波段內的極小值和極大值,然後如果發現另一個更小的極小值後,再找出從此極小值開始後的極大值;最終我們比較的是這些極大值和極小值的差中的最大值。

class Solution {
    public int maxProfit(int[] prices) {
        int maxprofit = 0, temMax = 0, minprice = Integer.MAX_VALUE;
        for (int i = 0; i < prices.length; i++) {
            if (prices[i] < minprice) { //一旦找到更小的值,則開始從此點找極大值
                minprice = prices[i]; //始終存的的是已發現的最小值
                temMax = 0;//重新開始計算差值(這一步是可以忽略的)
            } else {
                temMax = prices[i] - minprice;
                maxprofit = Math.max(maxprofit, temMax);//和之前的最大利潤做比較
            }
        }
        return maxprofit;
    }
}

優化

以上邏輯等價於如下形式:

class Solution {
    public int maxProfit(int[] prices) {
        int maxprofit = 0, minprice = Integer.MAX_VALUE;
        for (int price : prices) {
            if (price < minprice) { //一旦找到更小的值,則開始從此點找極大值
                minprice = price; //始終存的的是已發現的最小值
            } else {
                maxprofit = Math.max(maxprofit, price - minprice);//和之前的最大利潤做比較
            }
        }
        return maxprofit;
    }
}

也等價於如下形式,雖然這種形式程式碼量小了一些,但這種形式其實計算多個很多:

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

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

陣列 貪心演算法

題目

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

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

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

波峰波谷法

class Solution {
    public int maxProfit(int[] prices) {
        int maxprofit = 0, minprice = Integer.MAX_VALUE, maxprice = Integer.MAX_VALUE;
        boolean findMin = true; //找波谷還是波峰
        for (int i = 0; i < prices.length; i++) {
            if (findMin) { //找波谷
                if (prices[i] < minprice) { //有更低的波谷
                    minprice = prices[i]; //更新購買價格
                    maxprice = prices[i];
                } else { //比波谷的值大,那麼我們就找波峰
                    maxprice = prices[i]; //更新賣出價格
                    findMin = false;
                }
            } else { //找波峰
                if (prices[i] > maxprice) { //有更高的波峰
                    maxprice = prices[i]; //更新賣出價格
                } else { //比波峰小,那麼我們就在之前賣出,然後在這裡買入
                    maxprofit += (maxprice - minprice); //更新收益
                    minprice = prices[i]; //重置所有狀態
                    maxprice = prices[i];
                    findMin = true;
                }
            }
        }
        return maxprofit + (maxprice - minprice);//防止最後在找波峰過程中結束,導致沒有賣出的問題
    }
}

時間複雜度:O(n)
空間複雜度:O(1)

波峰波谷法優化

上面的程式碼實際上有很多運算時可以忽略的,可以優化為如下邏輯

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length <= 1) return 0;
        
        int i = 0, valley = prices[0], peak = prices[0], maxprofit = 0;
        while (i < prices.length - 1) {
            while (i < prices.length - 1 && prices[i] >= prices[i + 1]) i++; //有更低的波谷
            valley = prices[i]; //更新購買價格
            while (i < prices.length - 1 && prices[i] <= prices[i + 1]) i++; //有更高的波峰
            peak = prices[i]; //更新賣出價格
            maxprofit += peak - valley;
        }
        return maxprofit;
    }
}

累加法

我們不需要跟蹤峰值和谷值對應的成本以及最大利潤,我們可以直接繼續增加陣列的連續數字之間的差值,如果第二個數字大於第一個數字,我們獲得的總和將是最大利潤。這種方法將簡化解決方案。

例如:[1, 7, 2, 3, 6, 7, 6, 7]
與此陣列對應的圖形是:

從上圖中,我們可以觀察到 A+B+C 的和等於差值 D 所對應的連續峰和谷的高度之差。

class Solution {
    public int maxProfit(int[] prices) {
        int maxprofit = 0;
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] > prices[i - 1]) maxprofit += prices[i] - prices[i - 1];
        }
        return maxprofit;
    }
}

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

給定一個數組,它的第 i 個元素是一支給定的股票在第 i 天的價格。
設計一個演算法來計算你所能獲取的最大利潤。你最多可以完成 兩筆 交易
注意: 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

動態規劃 二維陣列(不懂)

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length <= 1) return 0;

        int n = prices.length;
        int[][] g = new int[n][3], l = new int[n][3];
        for (int i = 1; i < n; i++) {
            int diff = prices[i] - prices[i - 1];
            for (int j = 1; j <= 2; j++) {
                l[i][j] = Math.max(g[i - 1][j - 1] + Math.max(diff, 0), l[i - 1][j] + diff);
                g[i][j] = Math.max(l[i][j], g[i - 1][j]);
            }
        }
        return g[n - 1][2];
    }
}

動態規劃 一維陣列(不懂)

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length <= 1) return 0;

        int n = prices.length;
        int[] g = new int[3], l = new int[3];
        for (int i = 0; i < n - 1; i++) {
            int diff = prices[i+1] - prices[i ];
            for (int j = 2; j >= 1; j--) {
                l[j] = Math.max(g[j - 1] + Math.max(diff, 0), l[j] + diff);
                g[j] = Math.max(l[j], g[j]);
            }
        }
        return g[2];
    }
}

買賣股票的最佳時機 IV -188

給定一個數組,它的第 i 個元素是一支給定的股票在第 i 天的價格。
設計一個演算法來計算你所能獲取的最大利潤。你最多可以完成 k 筆交易
注意: 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

方法宣告

class Solution {
    public int maxProfit(int k, int[] prices) {
        
    }
}

動態規劃(不懂)

class Solution {
    public int maxProfit(int k, int[] prices) {
        if (prices == null || prices.length <= 1) return 0;

        int n = prices.length;
        if (k >= n) return maxProfit(prices);

        int[] g = new int[k + 1], l = new int[k + 1];
        for (int i = 0; i < n - 1; i++) {
            int diff = prices[i + 1] - prices[i];
            for (int j = k; j >= 1; j--) {
                l[j] = Math.max(g[j - 1] + Math.max(diff, 0), l[j] + diff);
                g[j] = Math.max(l[j], g[j]);
            }
        }
        return g[k];
    }

    public int maxProfit(int[] prices) {
        int maxprofit = 0;
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] > prices[i - 1]) maxprofit += prices[i] - prices[i - 1];
        }
        return maxprofit;
    }
}

2018-12-16