LeetCode-股票系列

LeetCode-股票系列

相关题目有:

121.买卖股票的最佳时机(剑指Offer63)-- 简单、中等
122.买卖股票的最佳时机Ⅱ – 简单
123.买卖股票的最佳时机Ⅲ – 困难
188.买卖股票的最佳时机Ⅳ – 困难
309.最佳买卖股票时机含冷冻期 – 中等
714.买卖股票的最佳时机含手续费 – 中等

题目描述

给定一个数组,它的第 i i i 个元素是一支给定股票第 i i i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

注意:你不能在买入股票前卖出股票。

来源:力扣(LeetCode)

分析

​ 题目要求最多完成一笔交易,即我们需要找到一个最低价 d i d_i di 天买入,一个最高价 d j d_j dj天卖出,并且 j > i j>i j>i。 我们可以从头到尾遍历数组,用一个变量 m i n p r i c e minprice minprice 来记录所遍历到的股票的最低价格,再用一个变量 p r o f i t profit profit 记录所获得的最大利润。时间复杂度 O ( n ) O(n) O(n), 空间复杂度 O ( 1 ) O(1) O(1)

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

题目描述

给定一个数组,它的第 i 个元素是一支给定股票第 i i i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)

来源:力扣(LeetCode)

分析

​ 前面的题目要求是最多只能完成一笔交易,现在改成不限制交易次数,但是不能同时参与多比交易,也即是说,必须在再次购买股票时出售之前的股票,求所获得的最大利润。由于不限制交易次数,我们只要遇到股票价格比之前高的便可以卖出。举个例子,假设股票价格是[2,3,4],我们可以第一天(股票价格 = 2)买入,第二天(股票价格 = 3)卖出,第二天(股票价格 = 3)买入,第三天(股票价格 = 4)卖出,利润为(3-2)+(4-3) = 2,这个结果等价于我们在第一天(股票价格 = 2)买入,第三天(股票价格 = 4)卖出, 利润为(4 - 2) = 2。于是我们可以遍历整个数组,如果发现今天的股票价格比昨天的高,我们就可以在昨天买入,在今天卖出。 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)

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

题目描述

给定一个数组,它的第 i i i 个元素是一支给定的股票在第 i i i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

来源:力扣(LeetCode)

分析

​ 解法一:贪心算法

​ 上一题是最多只能完成一笔交易,而这一题是最多完成两笔交易,那么在上一题的基础上,假设我们已经计算得到最多完成一笔交易的情况下所能获得的最大利润,那么这一题可以看成在上一题的基础上,我们将数组划分成前后两部分,分别表示前后两次交易的时间区间,然后我们在这两个区间分别按照第一题的方法求出所能获得的最大利润,再将这两个利润相加。两次遍历,第一次正向遍历数组,与第一题做法类似,我们可以用 m i n p r i c e minprice minprice记录当前所遍历到的股票最低价格,再用一个数组 p r o f i t [ n ] profit[n] profit[n] 记录每一天所能获得的最大利润,即 p r o f i t [ i ] = m a x ( p r o f i t [ i − 1 ] , p r i c e s [ i ] − m i n p r i c e ) profit[i] = max(profit[i-1],prices[i] - minprice) profit[i]=max(profit[i1],prices[i]minprice) ,接下来反向遍历数组,记录在第二次交易的情况下第 i i i天能获得的利润,与正向遍历相对应的,我们用一个变量 m a x p r i c e maxprice maxprice 表示当前所遍历到的股票最高价格,此时在第 i i i天之后进行第二次交易所获得的最大利润 p r o f i t [ i ] = m a x ( p r o f i t [ i + 1 ] , m a x p r i c e − p r i c e [ i ] ) profit[i] = max(profit[i+1], maxprice - price[i]) profit[i]=max(profit[i+1],maxpriceprice[i])。则两次交易的利润之和为 p r o f i t [ i ] + p r o f i t [ i − 1 ] profit[i] + profit[i-1] profit[i]+profit[i1],我们用一个变量 a n s ans ans 记录当前所获得的最大利润,则 a n s = m a x ( a n s , p r o f i t [ i ] + p r o f i t [ i − 1 ] ) ans = max(ans,profit[i] + profit[i-1]) ans=max(ans,profit[i]+profit[i1])。时间复杂度为 O ( n ) O(n) O(n), 空间复杂度为 O ( n ) O(n) O(n)

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.size() <= 1){
            return 0;
        }
        int length = prices.size();
        vector<int> profit;
        profit.push_back(0);
        int min_price = prices[0];
        for (int i = 1; i < length; i++){
            min_price = min(min_price, prices[i]);
            profit.push_back(max(profit[i-1], prices[i] - min_price));
        }
        int ans = profit[length - 1];
        profit[length - 1] = 0;
        int max_price = prices[length - 1];
        for (int i = length - 2; i > 0; i--){
            max_price = max(max_price, prices[i]);
            profit[i] = max(profit[i+1], max_price - prices[i]);
            ans = max(ans, profit[i] + profit[i-1]);
        }
        return ans;
    }
};

​ 解法二:动态规划,这种解法适用于题目要求最多完成 k k k笔交易,于是我们结合下面这道题来讲。

题目描述

给定一个整数数组 p r i c e s prices prices ,它的第 i i i 个元素 p r i c e s [ i ] prices[i] prices[i] 是一支给定的股票在第 i i i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

来源:力扣(LeetCode)

分析

​ 当 k = 1 k = 1 k=1时,题目等同于 121.买卖股票的最佳时机。考虑 k > = 2 k >= 2 k>=2的情况,我们用动态规划来解这道题。用数组 d p [ i ] [ j ] [ 0 ] dp[i][j][0] dp[i][j][0] d p [ i ] [ j ] [ 1 ] dp[i][j][1] dp[i][j][1]分别表示在第 i i i 天进行第 j j j 次交易后持股状态和不持股状态所获得的最大利润。初始状态如下:

d p [ 0 ] [ 0 ] [ 0 ] = − p r i c e s [ i ] dp[0][0][0] = -prices[i] dp[0][0][0]=prices[i] d p [ 0 ] [ 0 ] [ 1 ] = 0 dp[0][0][1] = 0 dp[0][0][1]=0 d p [ i ] [ 0 ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] [ 0 ] , − p r i c e s [ i ] ) , i > 0 dp[i][0][0] = max(dp[i-1][0][0], -prices[i]), i > 0 dp[i][0][0]=max(dp[i1][0][0],prices[i]),i>0 d p [ i ] [ 0 ] [ 1 ] = m a x ( d p [ i − 1 ] [ 0 ] [ 1 ] , p r i c e s [ i ] − m i n p r i c e ) , i > 0 dp[i][0][1] = max(dp[i-1][0][1], prices[i] - minprice), i > 0 dp[i][0][1]=max(dp[i1][0][1],prices[i]minprice),i>0

其中, m i n p r i c e = m i n ( m i n p r i c e , p r i c e [ i ] ) minprice = min(minprice, price[i]) minprice=min(minprice,price[i])

状态转移方程如下 ( i > 0 , j > 0 ) (i>0,j>0) (i>0,j>0):

i i i 天进行第 j j j 次交易后处于持股状态有两种情况,一是第 i − 1 i-1 i1 天进行第 j j j 次交易后处于持股状态,不进行交易,二是第 i − 1 i-1 i1天进行第 j − 1 j-1 j1次交易后处于不持股状态,买入股票,则所能获得的最大利润为

d p [ i ] [ j ] [ 0 ] = m a x ( d p [ i − 1 ] [ j − 1 ] [ 1 ] − p r i c e s [ i ] , d p [ i − 1 ] [ j ] [ 0 ] ) dp[i][j][0] = max(dp[i-1][j-1][1] - prices[i], dp[i-1][j][0]) dp[i][j][0]=max(dp[i1][j1][1]prices[i],dp[i1][j][0])

i i i 天进行第 j j j 次交易后处于不持股状态有两种情况, 一是第 i − 1 i-1 i1 天进行第 j j j 次交易后处于持股状态, 卖出股票,二是第 i − 1 i-1 i1 天进行第 j j j 次交易后处于不持股状态,不进行交易,则所获得的最大利润为

d p [ i ] [ j ] [ 1 ] = m a x ( d p [ i − 1 ] [ j ] [ 0 ] + p r i c e s [ i ] , d p [ i − 1 ] [ j ] [ 0 ] ) dp[i][j][1] = max(dp[i-1][j][0] + prices[i], dp[i-1][j][0]) dp[i][j][1]=max(dp[i1][j][0]+prices[i],dp[i1][j][0])

最后我们只需要找出在最后一天完成第 j j j笔交易时的最大值,即 m a x ( d p [ n − 1 ] [ j ] [ 1 ] , 0 < = j < k ) max(dp[n-1][j][1], 0<=j<k) max(dp[n1][j][1],0<=j<k)

此时是时间复杂度为 O ( n ∗ k ) O(n*k) O(nk), 空间复杂度为 O ( n ∗ k ) O(n*k) O(nk)

接下来进行优化。假设数组长度为 n n n, 当 k > = n / 2 k >= n/2 k>=n/2时,相当于无限制交易次数,题目等同于 122.买卖股票的最佳时机Ⅱ。此时不需要用动态规划,可以把空间复杂度降低为 O ( 1 ) O(1) O(1)。考虑到第 i i i 天的状态只与第 i − 1 i-1 i1 天的状态有关,我们可以用动态数组将原本的三维数组降低为二维数组。此时初始状态和状态转移方程如下:

d p [ 0 ] [ 0 ] = − p r i c e s [ i ] dp[0][0] = -prices[i] dp[0][0]=prices[i] d p [ 0 ] [ 1 ] = 0 dp[0][1] = 0 dp[0][1]=0 i = 0 i = 0 i=0

d p [ 0 ] [ 0 ] = m a x ( d p [ 0 ] [ 0 ] , − p r i c e s [ i ] ) , i > 0 dp[0][0] = max(dp[0][0], -prices[i]), i > 0 dp[0][0]=max(dp[0][0],prices[i]),i>0

d p [ 0 ] [ 1 ] = m a x ( d p [ 0 ] [ 1 ] , p r i c e s [ i ] − m i n p r i c e ) , i > 0 dp[0][1] = max(dp[0][1], prices[i] - minprice), i > 0 dp[0][1]=max(dp[0][1],prices[i]minprice),i>0

d p [ j ] [ 0 ] = m a x ( d p [ j − 1 ] [ 1 ] − p r i c e s [ i ] , d p [ j ] [ 0 ] ) dp[j][0] = max(dp[j-1][1] - prices[i], dp[j][0]) dp[j][0]=max(dp[j1][1]prices[i],dp[j][0])

d p [ j ] [ 1 ] = m a x ( d p [ j ] [ 0 ] + p r i c e s [ i ] , d p [ j ] [ 0 ] ) dp[j][1] = max(dp[j][0] + prices[i], dp[j][0]) dp[j][1]=max(dp[j][0]+prices[i],dp[j][0])

此时的空间复杂度为 O ( k ) O(k) O(k)

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        if (prices.size() == 0 || k == 0){
            return 0;
        }
        int days = prices.size();
        int dp[k][2];
        int minprice = prices[0];
        for (int i = 0; i < days; i++){
            for (int j = 0; j < k; j++){
                if (i == 0){
                    dp[j][0] = -prices[0];
                    dp[j][1] = 0;
                }
                if (j == 0){
                    dp[j][0] = max(dp[j][0], -prices[i]);
                    lowest = min(minprice, prices[i]);
                    dp[j][1] = max(dp[j][1], prices[i] - minprice);
                }
                else{
                    dp[j][0] = max(dp[j-1][1] - prices[i], dp[j][0]);
                    dp[j][1] = max(dp[j][0]+prices[i],dp[j][1]);
                }
            }
        }
        int ans = 0;
        for (int j = 0; j < k; j++){
            ans = max(ans, dp[j][1]);
        }
        return ans;
    }
};

题目描述

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

​ 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
​ 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:

来源:力扣(LeetCode)

分析

​ 考虑用动态规划来做。分别用 d p [ i ] [ 0 ] , d p [ i ] [ 1 ] dp[i][0],dp[i][1] dp[i][0],dp[i][1] d p [ i ] [ 2 ] dp[i][2] dp[i][2]表示第 i i i天结束时的三个状态:持股、不持股不处于冷冻期和不持股处于冷冻期所能获得的最大利润。初始状态为: d p [ 0 ] [ 0 ] = − p r i c e s [ 0 ] dp[0][0] = -prices[0] dp[0][0]=prices[0] d p [ 0 ] [ 1 ] = 0 dp[0][1] = 0 dp[0][1]=0 d p [ 0 ] [ 2 ] = 0 dp[0][2] = 0 dp[0][2]=0。状态转移方程如下:

(1)第 i i i天持股有两种情况,一是第 i − 1 i-1 i1天持股第 i i i天不交易,二是第 i − 1 i-1 i1天不持股在第 i i i天买入股票,所能获得的最大利润为 m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] − p r i c e s [ i ] ) max(dp[i-1][0],dp[i-1][1] - prices[i]) max(dp[i1][0],dp[i1][1]prices[i])

(2)第 i i i天不持股不处于冷冻期有两种情况,一是第 i − 1 i-1 i1天不持股不处于冷冻期第 i i i天不交易,二是第 i − 1 i-1 i1天不持股处于冷冻期第 i i i天不交易,所能获得的最大利润为 m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 2 ] ) max(dp[i-1][1],dp[i-1][2]) max(dp[i1][1],dp[i1][2])

(3)第 i i i天不持股处于冷冻期只有一种情况,即在第 i − 1 i-1 i1持股的情况下卖出股票,所能获得的利润为 d p [ i ] [ 2 ] = d p [ i − 1 ] [ 0 ] + p r i c e s [ i ] dp[i][2] = dp[i-1][0] + prices[i] dp[i][2]=dp[i1][0]+prices[i]

返回最后一天三种状态所能获得的最大利润。

此时的时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)。而从上面的状态转移方程来看,第 i i i天的状态只与前一天的状态有关,可以考虑用动态数组将空间复杂度优化为 O ( 1 ) O(1) O(1)

代码如下:

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

题目描述

给定一个整数数组 p r i c e s prices prices,其中第 i i i 个元素代表了第 i i i 天的股票价格 ;非负整数 f e e fee fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

来源:力扣(LeetCode)

分析

​ 我们以天数为横轴,股票价格为纵轴,将股票曲线表示出来,当不考虑手续费的时候,所获得的最大利润等于股票曲线的每一个“上坡”的两个端点的价格之差。比如,对于数组 [ 1 , 3 , 2 , 8 , 4 , 9 ] [1,3,2,8,4,9] [1,3,2,8,4,9],总共有三个“上坡”, 分别是 ( 1 , 3 ) , ( 2 , 8 ) , ( 4 , 9 ) (1,3),(2,8),(4,9) (1,3),(2,8),(4,9),在不考虑手续费的情况下,所能获得的最大利润为 ( 3 − 1 ) + ( 8 − 2 ) + ( 9 − 4 ) (3-1)+(8-2)+(9-4) (31)+(82)+(94)。在考虑手续费的情况下, 当手续费为 2 2 2的时候,如果第二个上坡的坡顶价格比第一个坡顶高,我们需要考虑,将连续的上坡合并起来交易的利润与分别交易的利润哪一种情况下所能获得的利润最大。比如对于前两个“上坡” ( 1 , 3 ) , ( 2 , 8 ) (1,3),(2,8) (1,3),(2,8),如果我们交易两次,获得的利润为 ( 3 − 1 − 2 ) + ( 8 − 2 − 2 ) = 4 (3 - 1 - 2) + (8 - 2 - 2) = 4 (312)+(822)=4, 考虑到 8 > 3 8>3 8>3,如果我们交易一次,获得的利润为 ( 8 − 1 − 2 ) = 5 (8-1-2) = 5 (812)=5, 也即是说,我们合并两次交易所能获得的利润最大。所以,我们可以用贪心算法来解这道题,用变量 p r i c e price price 记录上一次交易股票价格。用 m i n p r i c e minprice minprice记录在上一次交易后所遍历到的最低股票价格,即坡底。 从前往后遍历数组,当遇到比 m i n p r i c e minprice minprice更低的股票价格,则将 m i n p r i c e minprice minprice更新为当前的股票价格,这表示后面的交易不需要与上一次合并了。我们找出数组里面的每一个上坡,然后比较直接交易或者与上一次交易合并或者不交易哪一种情况下能获得的利润最大。每一次交易完成,将 m i n p r i c e minprice minprice p r i c e price price 更新为当天的股票价格。时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int length = prices.size();
        if (length == 0){
            return 0;
        }
        int profit = 0;
        int minprice = INT_MAX;
        int price = INT_MAX;
        for (int i = 0; i < length; i++){
            if (prices[i] < minprice){
                minprice = prices[i];
            }
            while(i < length -1 && prices[i+1] >= prices[i]){
                i++;//找上坡
            }
            if (prices[i] > price || prices[i] - minprice - fee > 0){
                 int myadd = prices[i] - price > prices[i] - minprice - fee? prices[i] - price : prices[i] - minprice - fee;
                 profit += myadd;
                 price = prices[i];
                 minprice = prices[i]; 
            }
        }
        return profit;
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值