买卖股票问题

一、LeetCode121:买卖股票的最佳时机

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

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

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

示例1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格

示例2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为0

解析:

找出给定数组中两个数字之间的最大差值

题解:

    public int maxProfit(int[] prices) {
        int minPrice = Integer.MAX_VALUE;
        int maxProfit = 0;
        for (int i = 0; i < prices.length; ++i) {
            if (prices[i] < minPrice)
                minPrice = prices[i];
            maxProfit = Math.max(maxProfit, prices[i] - minPrice);
        }
        return maxProfit;
    }

二、LeetCode122:买卖股票的最佳时机 II

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

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

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

示例1:

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 

示例2:

输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票

示例3:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为0

解析:

遍历整个股票交易日价格列表,策略是所有上涨交易日都买卖(赚到所有利润),所有下降交易日都不买卖(永不亏钱)

题解:

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

三、LeetCode123:买卖股票的最佳时机 III

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

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

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

示例1:

输入: [3,3,5,0,0,3,1,4]
输出: 6
解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 
     随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 

示例2:

输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4  
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票

示例3:

输入: [7,6,4,3,1] 
输出: 0 
解释: 在这个情况下, 没有交易完成, 所以最大利润为 0

解析:

dp[i][k]表示前i天最多交易k次的最高收益

状态转移方程:

dp[i][k] = Math.max(dp[i - 1][k], prices[i] - prices[j] + dp[j][k - 1]) (k<i)

题解:

    public int maxProfit(int[] prices) {
        if (prices.length == 0) return 0;
        //dp[i][k] = Math.max(dp[i - 1][k], prices[i] - prices[j] + dp[j][k - 1]) (k<i)
        int K = 2;
        int[][] dp = new int[prices.length][K + 1];
        for (int k = 1; k <= K; ++k) {
            for (int i = 1; i < prices.length; ++i) {
                Integer max = Integer.MIN_VALUE;
                for (int j = 0; j <= i; ++j) {
                    max = Math.max(max, prices[i] - prices[j] + dp[j][k - 1]);
                }
              	//比较不操作和选择一天买入的哪个值更大
                dp[i][k] = Math.max(dp[i - 1][k], max);
            }
        }
        return dp[prices.length - 1][K];
    }

优化时间复杂度:

    public int maxProfit(int[] prices) {
        if (prices.length == 0) return 0;
        int K = 2;
        int[][] dp = new int[prices.length][K + 1];
        for (int k = 1; k <= K; ++k) {
            Integer min = prices[0];
            for (int i = 1; i < prices.length; ++i) {
                min = Math.min(min, prices[i] - dp[i][k - 1]);
                dp[i][k] = Math.max(dp[i - 1][k], prices[i] - min);
            }
        }
        return dp[prices.length - 1][K];
    }

优化空间复杂度:

    public int maxProfit(int[] prices) {
        if (prices.length == 0) return 0;
        int K = 2;
        int[] dp = new int[K + 1];
        int[] min = new int[K + 1];
        for (int i = 1; i <= K; ++i)
            min[i] = prices[0];
        for (int i = 1; i < prices.length; ++i) {
            for (int k = 1; k <= K; ++k) {
                min[k] = Math.min(min[k], prices[i] - dp[k - 1]);
                dp[k] = Math.max(dp[k], prices[i] - min[k]);
            }
        }
        return dp[K];
    }

四、LeetCode188:买卖股票的最佳时机 IV

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

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

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

示例1:

输入: [2,4,1], k = 2
输出: 2
解释: 在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 

示例2:

输入: [3,2,6,5,0,3], k = 2
输出: 7
解释: 在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 
     随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 

解析:

按照LeetCode123的思路改造了一下,代码如下:

    public int maxProfit(int k, int[] prices) {
        if (prices.length == 0) return 0;
        int[] dp = new int[k + 1];
        int[] min = new int[k + 1];
        for (int i = 1; i <= k; ++i)
            min[i] = prices[0];
        for (int i = 1; i < prices.length; ++i) {
            for (int j = 1; j <= k; ++j) {
                min[j] = Math.min(min[j], prices[i] - dp[j - 1]);
                dp[j] = Math.max(dp[j], prices[i] - min[j]);
            }
        }
        return dp[k];
    }

提交时发现超出内存限制了

在这里插入图片描述

申请了两个k+1大的数组,当k太大时超出内存限制了

如果数组大小是n,然后一天买,一天卖,最多就是n/2次交易(买入然后卖出算一次交易),如果最多交易k次,k达到了n/2,也就是最多的交易次数,代表可以交易任意次,和LeetCode122要求相同,所以最终的解法根据 k < n / 2 k<n/2 k<n/2 k > = n / 2 k>=n/2 k>=n/2分开求解

题解:

    public int maxProfit(int k, int[] prices) {
        if (prices.length == 0) return 0;
        if (k > prices.length / 2) return maxProfit(prices);
        int[] dp = new int[k + 1];
        int[] min = new int[k + 1];
        for (int i = 1; i <= k; ++i)
            min[i] = prices[0];
        for (int i = 1; i < prices.length; ++i) {
            for (int j = 1; j <= k; ++j) {
                min[j] = Math.min(min[j], prices[i] - dp[j - 1]);
                dp[j] = Math.max(dp[j], prices[i] - min[j]);
            }
        }
        return dp[k];
    }

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

五、LeetCode309:最佳买卖股票时机含冷冻期

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

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

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

卖出股票后,你无法在第二天买入股票(即冷冻期为1天)

示例:

输入: [1,2,3,0,2]
输出: 3 
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

解析:

每天可能存在三种状态:

hold:继续持有股票
sold:卖出股票
rest:什么都不做

转换关系如下:

在这里插入图片描述

hold: 可由两个情况转换来
前一天hold,当日rest
前一天rest,当日买入股票变为hold

sold:
前一天hold,当日卖出股票

rest:
前一天sold,当日必须rest
前一天rest,当日继续rest

题解:

    public int maxProfit(int[] prices) {
        //hold:继续持有股票 hold[i]=Math.max(hold[i - 1], rest[i - 1] - prices[i])
        //sold:卖出股票 sold[i]=hold[i - 1] + prices[i]
        //rest:什么都不做 rest[i]=Math.max(rest[i - 1], sold[i - 1])
        int sold = 0, rest = 0, hold = Integer.MIN_VALUE;
        for (int price : prices) {
            int preSold = sold;
            sold = hold + price;
            hold = Math.max(hold, rest - price);
            rest = Math.max(rest, preSold);
        }
        return Math.max(sold, rest);
    }

六、LeetCode714:买卖股票的最佳时机含手续费

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

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

返回获得利润的最大值

示例1:

输入: prices = [1, 3, 2, 8, 4, 9], fee = 2
输出: 8
解释: 能够达到的最大利润: 
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8

解析:

cash:不持有股票时的最大利润

hold:持有股票时的最大利润

在第i天时,我们需要根据第i-1天的状态来更新cash和hold的值。对于cash,可以保持不变,或者将手上的股票卖出,状态转移方程为

cash = Math.max(cash, hold + prices[i] - fee)

对于hold,可以保持不变,或者买入这一天的股票,状态转移方程为

hold = Math.max(hold, cash - prices[i])

题解:

    public int maxProfit(int[] prices, int fee) {
        int cash = 0, hold = -prices[0];
        for (int i = 1; i < prices.length; ++i) {
            cash = Math.max(cash, hold + prices[i] - fee);
            hold = Math.max(hold, cash - prices[i]);
        }
        return cash;
    }
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邋遢的流浪剑客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值