leetcode_[python/C++]_121/122/123/188.Best Time to Buy and Sell Stock I/II/III/IV

本文详细分析了LeetCode中四道股票交易问题,包括最多一次、任意次数、最多两次及最多k次交易时如何找到最大利润。重点介绍了动态规划等高效解法,并提供了不同语言的实现思路,所有问题都能在O(n)时间复杂度内解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

121. Best Time to Buy and Sell Stock
[题目]
Say you have an array for which the ith element is the price of a given stock on day i.

If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.


122. Best Time to Buy and Sell Stock II
Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).


123. Best Time to Buy and Sell Stock III
Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).


188. Best Time to Buy and Sell Stock IV
Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most k transactions.

Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).


[分析]

首先这四道题是类似的题型,逐步深入

第一道题:求一次买入卖出的最大收益

第二道题:可以买入卖出任意多次,求最大收益

第三道题:限制买入卖出次数最多为2次,求最大收益

第四道题:限制买入卖出次数最多为k次,求最大收益


第一道题:
解法1:

int maxProfit(vector<int>& prices) {
        int size = prices.size();
        if(size == 0)
        return 0;
        int low = prices[0];
        int high = prices[0];
        int temp_low=low;
        for(int i=1;i<size;i++)
        {
            if(prices[i]-temp_low>=high-low)
            {
                low = temp_low;
                high = prices[i];
            }
            else if(prices[i]>high)
            high = prices[i];
            else if(prices[i]<=low)
            {
                if(prices[i]<=temp_low) temp_low = prices[i];
            }


        }
        if(high-low<=0)
        return 0;
        else
        return high - low;
    }

解法2:

int maxProfit(vector<int> &prices) {
    int maxPro = 0;
    int minPrice = INT_MAX;
    for(int i = 0; i < prices.size(); i++){
        minPrice = min(minPrice, prices[i]);
        maxPro = max(maxPro, prices[i] - minPrice);
    }
    return maxPro;
}

或者:

int maxProfit(vector<int>& prices) {
        int minPrice = INT_MAX;
        int maxProfit = 0;

        for (int currPrice : prices){
            if (currPrice < minPrice) minPrice = currPrice;
            else maxProfit = max(currPrice - minPrice, maxProfit);
        }

        return maxProfit;
    }

解法3(动态规划):

int maxProfit(vector<int>& prices) {
      int Nsize = prices.size();
      int sumCurrent = 0;
      int sumIncrease = 0;

          for(int i = 1; i < Nsize; i++)
          {
              sumIncrease = sumIncrease + prices[i] - prices[i-1];
             if(sumIncrease > sumCurrent) sumCurrent = sumIncrease;
             if(sumIncrease < 0) sumIncrease = 0; 

          }
        return sumCurrent;   
    }

注:第三种解法效率最高


第二道题:
解法1:
从前往后(python):

def maxProfit(self, prices):
        return sum(max(prices[i + 1] - prices[i], 0) for i in range(len(prices) - 1))

从前往后(C++):

int maxProfit(vector<int>& prices) {
        int size = prices.size();
        if ( size == 0 || size == 1 ) return 0;
        int low = prices[0] , max_profit = 0;
        for ( int p : prices ){
            if ( p > low ) max_profit += p - low;
            low = p;
        }
        return max_profit;
    }

从前往后2(C++):

int maxProfit(vector<int>& prices) {
        if ( prices.size() == 0 ) return 0;
        int max_profit = 0;
        for ( int i = 0 ; i < prices.size() - 1 ; i ++ ){
            max_profit += prices[i+1] > prices[i] ? prices[i+1] - prices[i] : 0;
        }
        return max_profit;
    }

解法2:
从后往前:

int maxProfit(vector<int>& prices) {
        int size = prices.size();
        if ( size == 0 || size == 1 ) return 0;
        int high = prices[size - 1] , max_profit = 0;
        for ( int i = size - 2 ; i >= 0 ; i -- ){
            if ( prices[i] > prices[i+1] ){
                max_profit += high - prices[i+1];
                high = prices[i];
            }
        }
        if ( prices[0] < high ) max_profit += high - prices[0];
        return max_profit;
    }

两种思路都是O(n) time O(1) space,效率一样


第三道题:
思路:
找出从0到i的最大收益profit[i]
找出从i到size-1的最大收益profit[i]
之和求最大值


解法1:
O(2n) time O(1) space

int maxProfit(vector<int>& prices) {
        int size = prices.size();
        if( size <= 1 ) return 0;
        int profit[size];
        memset(profit,0,sizeof(profit));
        int max_profit = 0;
        int Mins = prices[0];
        for( int i = 1 ; i < size ; i ++ ){
            if( prices[i] - Mins > max_profit ) max_profit = prices[i] - Mins;
            if( prices[i] < Mins ) Mins = prices[i];
            profit[i] = max_profit;
        }
        int max_profit_r = 0;
        int Maxs = prices[size - 1] ;
        for( int i = size - 1 ; i >= 0 ; i-- ){
            if( profit[i] + Maxs - prices[i] > max_profit_r ) max_profit_r = profit[i] + Maxs - prices[i];
            if( Maxs < prices[i] ) Maxs = prices[i];
        }
        return max_profit_r;

    }

解法2:
O(n) time O(1) space

int maxProfit(vector<int>& prices) {
        int size = prices.size();
        if( size <= 1 ) return 0;
        int sel_1 = 0 , sel_2 = 0;
        int buy_1 = -prices[0], buy_2 = -prices[0];
        int profit = 0 ;
        for( int i = 0 ; i < size ; i ++ ) {
            int last_sel = sel_1;
            sel_1 = max( sel_1 , prices[i] + buy_1 );
            buy_1 = max( -prices[i] , buy_1 );
            sel_2 = max( sel_2 , prices[i] + buy_2 );
            buy_2 = max( last_sel - prices[i] , buy_2 );
            profit = max( sel_1 , sel_2 );
        }
        return profit;

    }

注:这两种解法效率相当


第四道题:
这道题有一个注意的地方就是当k>=prices.size()/2时候,证明我们可以随意买入卖出,直接累加即可


解法1:
O(kn) space O(n) time

int maxProfit(int k, vector<int>& prices) {
        // f[k, ii] represents the max profit up until prices[ii] (Note: NOT ending with prices[ii]) using at most k transactions. 
        // f[k, ii] = max(f[k, ii-1], prices[ii] - prices[jj] + f[k-1, jj]) { jj in range of [0, ii-1] }
        //          = max(f[k, ii-1], prices[ii] + max(f[k-1, jj] - prices[jj]))
        // f[0, ii] = 0; 0 times transation makes 0 profit
        // f[k, 0] = 0; if there is only one price data point you can't make any money no matter how many times you can trade
        if (prices.size() <= 1) return 0;
        if ( k > prices.size()/2 ){
            int max_profit = 0;
            for( int i = 1 ; i < prices.size() ; i ++ ){
                max_profit += max( prices[i] - prices[i-1] , 0 );
            }
            return max_profit;
        }
        else {

            int maxProf = 0;
            vector<vector<int>> f(k+1, vector<int>(prices.size(), 0));
            for (int kk = 1; kk <= k; kk++) {
                int tmpMax = f[kk-1][0] - prices[0];
                for (int ii = 1; ii < prices.size(); ii++) {
                    f[kk][ii] = max(f[kk][ii-1], prices[ii] + tmpMax);
                    tmpMax = max(tmpMax, f[kk-1][ii] - prices[ii]);
                    maxProf = max(f[kk][ii], maxProf);
                }
            }
            return maxProf;
        }
    }

解法2:
O(kn) space O(n) time
动态规划(比上面的快一点)

int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        if(k == 0 || n <= 1){
            return 0;
        }
        if (k >=  n/2) {
            int maxPro = 0;
            for (int i = 1; i < n; i++) {
                if (prices[i] > prices[i-1])
                    maxPro += prices[i] - prices[i-1];
            }
            return maxPro;
        }
        vector<vector<int>> dp(k + 1, vector<int>(n + 1, 0));
        for(int i = 1; i <= k; ++i){
            int tmpMax = dp[i - 1][0] - prices[0];
            for(int j = 1; j <= n; ++j){
                dp[i][j] = max(dp[i][j - 1], tmpMax + prices[j - 1]);
                tmpMax = max(tmpMax, dp[i - 1][j - 1] - prices[j - 1]);
            }
        }
        return dp[k][n];
    }

解法3:
O(n) space O(n) time
比上面的快点

int maxProfit(int k, vector<int>& prices) {
    int n = prices.size();
    if (k>=n/2) {
        int sum = 0;
        for(int i=1; i<n; i++){
            if(prices[i] > prices[i-1]){
                sum += prices[i] - prices[i-1];
            }
        }
        return sum;
    }
    vector<int> buy(k+1, INT_MIN), sale(k+1, 0);
    for(int i=0; i<n; i++){
        for(int j=1; j<=k; j++){
            buy[j] = max(buy[j], sale[j-1]-prices[i]);
            sale[j] = max(sale[j], buy[j] + prices[i]);
        }
    }
    return sale[k];
}

解法4:
网上的一种解法,利用heap,stack
O(klogn)time ( 最快 )
贴出来一起学习

int maxProfit(int k, vector<int>& prices) {

        // Step 1: Find out all profit opportunities            
        vector<int> profits;
        stack<pair<int, int>> vps; // valley-peak pairs

        int v;
        int p = -1;
        for (;;) {
            // find next valley-peak pair
            for (v = p+1; (v+1 < prices.size()) && (prices[v] >= prices[v+1]); ++v);
            for (p = v  ; (p+1 < prices.size()) && (prices[p] <= prices[p+1]); ++p);

            if (v == p) { // v==p means that both v and p reach the end of the array
                break;
            }

            // Consider two transactions (v1, p1) (back of the stack) and (v2, p2) (the new-found).
            // If prices[v1] >= prices[v2], 
            // it is meaningless to combine the two transactions.
            // Save of profit of (v1, p1), and pop it out of the record.
            while ((!vps.empty()) && (prices[v] <= prices[vps.top().first])) {
                profits.push_back(prices[vps.top().second] - prices[vps.top().first]);
                vps.pop();
            }

            // If prices[v1]<prices[v2] and prices[p1]<prices[p2], 
            // then it is meaningful to combine the two transactions
            // update (v1, p1) to (v1, p2), and save the profit of (v2, p1)
            while ((!vps.empty()) && (prices[p] >= prices[vps.top().second])) {
                profits.push_back(prices[vps.top().second] - prices[v]);
                v = vps.top().first;
                vps.pop();
            }

            // save the new-found valley-peak pair
            vps.emplace(v, p);
        }

        // save all remaining profits
        while (!vps.empty()) {
            profits.push_back(prices[vps.top().second] - prices[vps.top().first]);
            vps.pop();
        }

        // Step 2: Calculate the k highest profits
        int ret;
        if (profits.size() <= k) {
            ret = accumulate(profits.begin(), profits.end(), 0);
        } else {
            nth_element(profits.begin(), profits.end() - k, profits.end());
            ret = accumulate(profits.end() - k, profits.end(), 0);
        }
        return ret;
    }

总结:这四道题虽然不断变化,但是基本的思想是一致的,而且,基本都是能够O(n) time的解法解决

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值