LeetCode股票系列

简介: LeetCode股票系列

股票系列


121. 买卖股票的最佳时机【简单】


相同题目:剑指 Offer 63. 股票的最大利润



题目意思:只能买入一次,卖出一次,最大利润


  • 思路二:贪心


时间复杂度:O(n)


空间复杂度:O(1)


class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int minPrice = INT_MAX;
        int maxProfit = 0;
        for (int price : prices) {   
            minPrice = min(minPrice, price);              // 动态更新最小价格
            maxProfit = max(maxProfit, price - minPrice);     
        }
        return maxProfit;
    }
};


思路三:动态规划


五部曲:


  1. 确定dp数组以及下标含义:


  • dp[i][0]表示第i天交易完后手里没有股票的最大利润


  • dp[i][1]表示第i天交易完后手里持有股票的最大利润


  1. 确定递推公式


  • dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);


  • dp[i][1] = max(dp[i-1][i], -prices[i]);


  1. 初始化


dp[i][0] = 0;


dp[i][1] = -prices[0];


时间复杂度:O(n)


空间复杂度:O(n)


class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        int dp[n][2];
        //初始化
        dp[0][0] = 0;           //第0天不持有股票的最大利润
        dp[0][1] = -prices[0];  //第0天持有股票的最大利润
        for (int i = 1; i < n; i++) {
            //第i天不持有股票的maxProfit = max(第i-1天不持有股票的maxProfit,第i-1天持有但第i天卖出时的maxProfit)
            dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);  
            //第i天持有股票的maxProfit = max(第i-1天持有股票的maxProfit,今天刚买入股票时的maxProfit)
            dp[i][1] = max(dp[i-1][1], -prices[i]);            
        }
        return dp[n-1][0];
    }
};


空间优化:


注意到上面的状态转移方程中,每一天的状态只与前一天的状态有关,而与更早的状态都无关,因此我们不必存储这些无关的状态,只需要将 dp[i-1][0]和 dp[i-1][1]存放在两个变量中,通过它们计算出 dp[i][0]和 dp[i][1]并存回对应的变量,以便于第 i+1天的状态转移即可。


空间复杂度:O(1)


class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        int sell = 0;
        int buy = -prices[0];
        for (int i = 1; i < n; ++i) {
            sell = max(sell, buy + prices[i]);
            buy = max(buy, -prices[i]);
        }
        return sell;
    }
};


122. 买卖股票的最佳时机 II【中等】



题目意思:


  • 只有一只股票!


  • 当前只有买股票或者买股票的操作


思路:把每一题的股价描点画成折线图,求所有上升线段的累加;借鉴376题


  • 贪心:


时间复杂度:O(n)


空间复杂度:O(1)


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


思路二:动态规划


时间复杂度:O(n)


空间复杂度:O(n)


五部曲:


  1. 确定dp数组以及下标含义:


  • dp[i][0]表示第i天交易完后手里没有股票的最大利润


  • dp[i][1]表示第i天交易完后手里持有股票的最大利润


  1. 确定递推公式


  • dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);


  • dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);


  1. 初始化


dp[i][0] = 0;


dp[i][1] = -prices[0];


class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        int dp[n][2];
        dp[0][0] = 0;            
        dp[0][1] = -prices[0];   
        for(int i = 1; i < n; i++) {
            dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);
            dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);
        }
        return dp[n-1][0];
    }
};


  • 空间优化:


空间复杂度:O(1)


class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        int sell = 0;
        int buy = -prices[0];
        for (int i = 1; i < n; ++i) {
            sell = max(sell, buy +prices[i]);
            buy = max(buy, sell -prices[i]);
        }
        return sell;
    }
};


123. 买卖股票的最佳时机 III【困难】



  • 思路:动态规划


时间复杂度:O(n)


空间复杂度:O(1)


看官方答案,能看懂


  • 未进行过任何操作


  • 只进行过一次买操作


  • 进行了一次买操作和一次卖操作,即完成了一笔交易


  • 在完成了一笔交易的前提下,进行了第二次买操作


  • 完成了全部两笔交易


buy1:


  • 在第i天不进行任何操作,保持原状态


  • 在未进行任何操作的前提下以prices[i]的价格买入股票


buy1 = max(buy1, -prices[i]);


sell1:


  • 在第i天不进行任何操作


  • 在只进行过一次买操作的前提下,以prices[i]的价格卖出股票


sell1 = mamx(sell1, buy1+prices[i]);


  class Solution {
  public:
      int maxProfit(vector<int>& prices) {
          //dp初始化
      int buy1 = -prices[0];
          int sell1 = 0;
          int buy2 = -prices[0];
          int sell2 = 0;
          for(int i = 1; i < prices.size(); ++i){
              //状态转移函数
              buy1 = max(buy1, -prices[i]);
              sell1 = max(sell1, buy1+prices[i]);
              buy2 = max(buy2, sell1-prices[i]);
              sell2 = max(sell2, buy2+prices[i]);
          }
          return sell2;
      }
  };


没进行空间优化之前是下面这样子:


  1. 没有操作


  1. 第一次买入


  1. 第一次卖出


  1. 第二次买入


  1. 第二次卖出


class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.size() == 0) return 0;
        vector<vector<int>> dp(prices.size(), vector<int>(5, 0));
        dp[0][1] = -prices[0];
        dp[0][3] = -prices[0];
        for (int i = 1; i < prices.size(); i++) {
            dp[i][0] = dp[i - 1][0];
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
            dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
            dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
            dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
        }
        return dp[prices.size() - 1][4];
    }
};


188. 买卖股票的最佳时机 IV【困难】



方法:动态规划


  1. 确定dp下标以及含义:


  • buy[i][j]:表示对于数组price[0~i]中的价格而言,恰好进行完j笔交易,并且当前手上持有一只股票,这时的最大利润


  • sell[i][j]:表示恰好进行完j笔交易,并且当前手上不持有股票,这时的最大利润


  1. 确定状态转移函数


  • buy[i][j] = max(buy[i-1][j], sell[i-1][j] - prices[i]);


  • ``sell[i][j] = max(sell[i-1][j], buy[i-1][j-1] + prices[i]);`


  1. dp初始化


buy[0][0] = -prices[0];


sell[0][0] = 0;


buy[0][1...k]是不合法的操作,设置一个不合理的值


sell[0][1...k]是不合法的操作


class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        if (prices.empty())  return 0;
        int n = prices.size();
        k = min(k, n / 2);   //n天最多完成 n/2笔交易  这行相当于剪枝,不加也行
        vector<vector<int>> buy(n, vector<int>(k + 1));
        vector<vector<int>> sell(n, vector<int>(k + 1));
        //初始化
        buy[0][0] = -prices[0];
        sell[0][0] = 0;
        for (int i = 1; i <= k; ++i) {
            buy[0][i] = sell[0][i] = INT_MIN / 2;  //为什么除2,因为后面有+操作,就会超出int值
        }
        for (int i = 1; i < n; ++i) {
            buy[i][0] = max(buy[i - 1][0], sell[i - 1][0] - prices[i]);
            for (int j = 1; j <= k; ++j) {
                buy[i][j] = max(buy[i - 1][j], sell[i - 1][j] - prices[i]);
                sell[i][j] = max(sell[i - 1][j], buy[i - 1][j - 1] + prices[i]);   
            }
        }
    //为什么返回这个?  最多完成k笔交易,但不是完成k笔交易时max
        return *max_element(sell[n - 1].begin(), sell[n - 1].end());
    }
};


空间优化:


class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        if (prices.empty())  return 0;
        int n = prices.size();
        k = min(k, n / 2);
        vector<int> buy(k + 1);
        vector<int> sell(k + 1);
        buy[0] = -prices[0];
        sell[0] = 0;
        for (int i = 1; i <= k; ++i) {
            buy[i] = sell[i] = INT_MIN / 2;
        }
        for (int i = 1; i < n; ++i) {
            buy[0] = max(buy[0], sell[0] - prices[i]);
            for (int j = 1; j <= k; ++j) {
                buy[j] = max(buy[j], sell[j] - prices[i]);
                sell[j] = max(sell[j], buy[j-1] + prices[i]);   
            }
        }
        return *max_element(sell.begin(), sell.end());
    }
};


时间复杂度:O(n * min(n,k))


空间复杂度:O(min(n,k))


309. 最佳买卖股票时机含冷冻期【中等】



思路:动态规划


1、确定dp数组及其含义


  • 目前持有一只股票,对应的累加最大收益记为f[i][0]


  • 目前不持有任何股票,并给处于冷冻期中,对应的累计最大收益记为f[i][1]


  • 目前不持有任何股票,并且不处于冷冻期中,对应的累计最大收益记为f[i][2]


2、确定状态转移函数


f[i][0] = max(f[i-1][0], f[i-1][2] - prices[i]);


f[i][1] = f[i-1][0] + prices[i];


f[i][2] = max(f[i-1][1], f[i-1][2]);


最终最大利润为:max(f[n-1][1], f[n-1][2])


3、初始化


f[0][0] = -prices[0];


f[0][1] = 0;


f[0][2] = 0;


时间复杂度:O(n)


空间复杂度:O(n)


class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.empty()) return 0;
        int n = prices.size();
        // f[i][0]: 手上持有股票的最大收益
        // f[i][1]: 手上不持有股票,并且处于冷冻期中的累计最大收益
        // f[i][2]: 手上不持有股票,并且不在冷冻期中的累计最大收益
        vector<vector<int>> f(n, vector<int>(3));
        f[0][0] = -prices[0];
        for (int i = 1; i < n; ++i) {
            f[i][0] = max(f[i-1][0], f[i-1][2] - prices[i]);
            f[i][1] = f[i-1][0] + prices[i];
            f[i][2] = max(f[i-1][1], f[i-1][2]);
        }
        return max(f[n-1][1], f[n-1][2]);
    }
};


空间优化:


空间复杂度:O(1)


class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.empty()) return 0;
        int n = prices.size();
        int f0 = -prices[0];
        int f1 = 0;
        int f2 = 0;
        for (int i = 1; i < n; ++i) {
            int newf0 = max(f0, f2 - prices[i]);
            int newf1 = f0 + prices[i];
            int newf2 = max(f1, f2);
            f0 = newf0;
            f1 = newf1;
            f2 = newf2;
        }
        return max(f1, f2);
    }
};


714. 买卖股票的最佳时机含手续费【中等】



  • 思路一:贪心


  • 时间复杂度:O(n)


  • 空间复杂度:O(1)


我们在做收获利润操作的时候其实有三种情况:


  • 情况一:收获利润的这一天并不是收获利润区间里的最后一天(不是真正的卖出,相当于持有股票),所以后面要继续收获利润。


  • 情况二:前一天是收获利润区间里的最后一天(相当于真正的卖出了),今天要重新记录最小价格了。


  • 情况三:不作操作,保持原有状态(买入,卖出,不买不卖)


class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int res = 0;
        int minPrice = prices[0]; // 记录最低价格
        for (int i = 1; i < prices.size(); i++) {
            // 情况二:相当于买入
            if (prices[i] < minPrice)  minPrice = prices[i];
            // 情况三:保持原有状态(因为此时买则不便宜,卖则亏本)
            //if (prices[i] >= minPrice && prices[i] <= minPrice + fee) {
            //    continue;
            //}
            // 计算利润,可能有多次计算利润,最后一次计算利润才是真正意义的卖出
            if (prices[i] > minPrice + fee) {
                res += prices[i] - minPrice - fee;
                minPrice = prices[i] - fee; // 情况一,这一步很关键
            }
        }
        return res;
    }
};


当我们卖出一支股票时,我们就立即获得了以相同价格并且免除手续费买入一支股票的权利


  • [x]


class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int buy = prices[0] + fee;
        int profit = 0;
        for (int i = 1; i < prices.size(); ++i) {
            if (prices[i] + fee < buy) {   //更新买入价
                buy = prices[i] + fee;
            }
            else if (prices[i] > buy) {    //收集利润
                profit += prices[i] - buy;
                buy = prices[i];
            }
        }
        return profit;
    }
};


思路二:动态规划


1、dp及其下标含义


dp[i][0]表示第i天交易完后手里没有股票的最大利润


dp[i][1]表示第i天交易完后手里持有一只股票的最大利润


2、转移函数


dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i] - fee);


dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);


3、初始化


dp[0][0] = 0;


dp[0][1] = -prices[0];


时间复杂度:O(n)


空间复杂度:O(n)


class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n = prices.size();
        vector<vector<int>> dp(n, vector<int>(2));
        //初始化
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for (int i = 1; i < n; ++i) {
            dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i] - fee);
            dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);
        }
        return dp[n-1][0];
    }
};


空间优化


空间复杂度:O(1)


class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n = prices.size();
        //初始化
        int sell = 0;
        int buy = -prices[0];
        //为什么从1开始,因为0已经初始化了 
        for (int i = 1; i < n; ++i) {
            sell = max(sell, buy + prices[i] - fee);
            buy = max(buy, sell - prices[i]);
        }
        return sell;
    }
};
相关文章
|
2月前
|
算法
《LeetCode》—— 买卖股票的最佳时机
《LeetCode》—— 买卖股票的最佳时机
|
4月前
|
SQL
leetcode-SQL-1393. 股票的资本损益
leetcode-SQL-1393. 股票的资本损益
36 0
|
4天前
|
算法 索引
leetcode代码记录(买卖股票的最佳时机
leetcode代码记录(买卖股票的最佳时机
10 1
|
4天前
|
算法
leetcode代码记录(买卖股票的最佳时机 IV
leetcode代码记录(买卖股票的最佳时机 IV
9 2
|
4天前
|
算法
leetcode代码记录(买卖股票的最佳时机 III
leetcode代码记录(买卖股票的最佳时机 III
12 5
|
4天前
leetcode代码记录(买卖股票的最佳时机 II
leetcode代码记录(买卖股票的最佳时机 II
8 1
|
4月前
|
Go
golang力扣leetcode 309.最佳买卖股票时机含冷冻期
golang力扣leetcode 309.最佳买卖股票时机含冷冻期
22 0
|
28天前
|
算法
【力扣】121. 买卖股票的最佳时机、122.买卖股票的最佳时机Ⅱ
【力扣】121. 买卖股票的最佳时机、122.买卖股票的最佳时机Ⅱ
|
1月前
|
算法
【力扣经典面试题】121. 买卖股票的最佳时机
【力扣经典面试题】121. 买卖股票的最佳时机
|
2月前
|
算法
leetcode121. 买卖股票的最佳时机
leetcode121. 买卖股票的最佳时机
14 0