美文网首页
股票交易问题合集

股票交易问题合集

作者: Phoebe_Liu | 来源:发表于2021-12-12 18:09 被阅读0次

    121. 买卖股票的最佳时机——只允许交易一次 动态规划 || 一次遍历

    给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
    你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
    返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
    输入:[7,1,5,3,6,4]
    输出:5
    解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
    注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
    第二种动态规划

    public class Solution {
    
        public int maxProfit(int[] prices) {
            int len = prices.length;
            // 特殊判断
            if (len < 2) {
                return 0;
            }
            int[][] dp = new int[len][2];
    
            // dp[i][0] 下标为 i 这天结束的时候,不持股,手上拥有的现金数
            // dp[i][1] 下标为 i 这天结束的时候,持股,手上拥有的现金数
    
            // 初始化:不持股显然为 0,持股就需要减去第 1 天(下标为 0)的股价
            dp[0][0] = 0;
            dp[0][1] = -prices[0];
    
            // 从第 2 天开始遍历
            for (int i = 1; i < len; i++) {
                dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
                dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
            }
            return dp[len - 1][0];
        }
    }
    
    

    122. 买卖股票的最佳时机 II —— 允许交易无数次,贪心||动态规划

    给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。
    设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
    注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
    输入: prices = [7,1,5,3,6,4]
    输出: 7
    解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
    随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
    贪心算法:
    时间复杂度 O(N) : 只需遍历一次price;
    空间复杂度 O(1): 变量使用常数额外空间。

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

    动态规划:
    dp[i][j] 表示到下标为 i 的这一天,持股状态为 j 时,我们手上拥有的最大现金数。
    确定初始值:起始如果什么都不做,dp[0][0] = 0;如果持有股票,当前拥有的现金数是当天股价的相反数,即 dp[0][1] = -prices[i];
    时间复杂度:O(N),这里 NN 表示股价数组的长度;
    空间复杂度:O(N),虽然是二维数组,但是第二维是常数,与问题规模无关。

    public class Solution {
    
        public int maxProfit(int[] prices) {
            int len = prices.length;
            if (len < 2) {
                return 0;
            }
    
            // 0:持有现金
            // 1:持有股票
            // 状态转移:0 → 1 → 0 → 1 → 0 → 1 → 0
            int[][] dp = new int[len][2];
    
            dp[0][0] = 0;
            dp[0][1] = -prices[0];
    
            for (int i = 1; i < len; i++) {
                // 这两行调换顺序也是可以的
                dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
                dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
            }
            return dp[len - 1][0];
        }
    }
    

    123. 买卖股票的最佳时机 III —— 最多买卖2次 动态规划 || 头尾双指针

    给定一个数组,它的第i 个元素是一支给定的股票在第 i天的价格。
    设计一个算法来计算你所能获取的最大利润。你最多可以完成 **两笔 **交易。
    注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
    输入:prices = [3,3,5,0,0,3,1,4]
    输出:6
    解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
    随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
    第一种:动态规划
    一天结束时,可能有持股、可能未持股、可能卖出过1次、可能卖出过2次、也可能未卖出过
    所以定义状态转移数组dp[天数][当前是否持股][卖出的次数]
    具体一天结束时的6种状态:

    • 未持股,未卖出过股票:说明从未进行过买卖,利润为0
      dp[i][0][0]=0
    • 未持股,卖出过1次股票:可能是今天卖出,也可能是之前卖的(昨天也未持股且卖出过)
      dp[i][0][1]=max(dp[i-1][1][0]+prices[i],dp[i-1][0][1])
    • 未持股,卖出过2次股票:可能是今天卖出,也可能是之前卖的(昨天也未持股且卖出过)
      dp[i][0][2]=max(dp[i-1][1][1]+prices[i],dp[i-1][0][2])
    • 持股,未卖出过股票:可能是今天买的,也可能是之前买的(昨天也持股)
      dp[i][1][0]=max(dp[i-1][0][0]-prices[i],dp[i-1][1][0])
    • 持股,卖出过1次股票:可能是今天买的,也可能是之前买的(昨天也持股)
      dp[i][1][1]=max(dp[i-1][0][1]-prices[i],dp[i-1][1][1])
    • 持股,卖出过2次股票:最多交易2次,这种情况不存在
      dp[i][1][2]=float('-inf')
    class Solution:
        def maxProfit(self, prices):
            if prices==[]:
                return 0
            length=len(prices)
            #结束时的最高利润=[天数][是否持有股票][卖出次数]
            dp=[ [[0,0,0],[0,0,0] ] for i in range(0,length) ]
            #第一天休息
            dp[0][0][0]=0
            #第一天买入
            dp[0][1][0]=-prices[0]
            # 第一天不可能已经有卖出
            dp[0][0][1] = float('-inf')
            dp[0][0][2] = float('-inf')
            #第一天不可能已经卖出
            dp[0][1][1]=float('-inf')
            dp[0][1][2]=float('-inf')
            for i in range(1,length):
                #未持股,未卖出过,说明从未进行过买卖
                dp[i][0][0]=0
                #未持股,卖出过1次,可能是今天卖的,可能是之前卖的
                dp[i][0][1]=max(dp[i-1][1][0]+prices[i],dp[i-1][0][1])
                #未持股,卖出过2次,可能是今天卖的,可能是之前卖的
                dp[i][0][2]=max(dp[i-1][1][1]+prices[i],dp[i-1][0][2])
                #持股,未卖出过,可能是今天买的,可能是之前买的
                dp[i][1][0]=max(dp[i-1][0][0]-prices[i],dp[i-1][1][0])
                #持股,卖出过1次,可能是今天买的,可能是之前买的
                dp[i][1][1]=max(dp[i-1][0][1]-prices[i],dp[i-1][1][1])
                #持股,卖出过2次,不可能
                dp[i][1][2]=float('-inf')
            return max(dp[length-1][0][1],dp[length-1][0][2],0)
    

    第二种:头尾双指针
    以i为分界线,[0,i]最大 + [i+1,len-1]最大。
    从左向右遍历很简单的能求出[0,i]最大,从左向右遍历过程中记录最小值,当前值与最小值求差,这个差的最大值就是[0,i]最大
    类似的能求出[i+1,len-1]最大,倒着做。为了便于查找,用数组maxs[]记录每个位置右侧差的最大值。
    实际编码中我选择先做[i+1,len-1]最大。在做[0,i]最大过程中,结合maxs[]求出答案。

    class Solution {
        public int maxProfit(int[] prices) {
            int len = prices.length;
            int max = 0;
            int[] maxs = new int[len + 1];
            for (int i = len - 1; i >= 0; i--) {
                int price = prices[i];
                max = Math.max(max, price);// [i,len-1]位置右侧的最大值
                maxs[i] = Math.max(maxs[i + 1], max - price);// [i,len-1]最大差值
            }
            int min = Integer.MAX_VALUE;
            max = 0;
            for (int i = 0; i < len; i++) {
                int price = prices[i];
                min = Math.min(min, price);// [0,i]最小值
                max = Math.max(max, price - min + maxs[i + 1]);// [0,i]最大值 + [i+1,len-1]最大值
            }
            return max;
        }
    }
    
    class Solution {
        public int maxProfit(int k, int[] prices) {
            if (prices.length == 0) {
                return 0;
            }
    
            int n = prices.length;
            k = Math.min(k, n / 2);
            int[][] buy = new int[n][k + 1];
            int[][] sell = new int[n][k + 1];
    
            buy[0][0] = -prices[0];
            sell[0][0] = 0;
            for (int i = 1; i <= k; ++i) {
                buy[0][i] = sell[0][i] = Integer.MIN_VALUE / 2;
            }
    
            for (int i = 1; i < n; ++i) {
                buy[i][0] = Math.max(buy[i - 1][0], sell[i - 1][0] - prices[i]);
                for (int j = 1; j <= k; ++j) {
                    buy[i][j] = Math.max(buy[i - 1][j], sell[i - 1][j] - prices[i]);
                    sell[i][j] = Math.max(sell[i - 1][j], buy[i - 1][j - 1] + prices[i]);   
                }
            }
    
            return Arrays.stream(sell[n - 1]).max().getAsInt();
        }
    }
    

    相关文章

      网友评论

          本文标题:股票交易问题合集

          本文链接:https://www.haomeiwen.com/subject/chnrfrtx.html