【代码训练营】day48 | 121. 买卖股票的最佳时机 & 122.买卖股票的最佳时机II

所用代码 java

买股票的最佳时机 LeetCode 121

题目链接:买股票的最佳时机 LeetCode 121 - 简单

思路

  • dp[i] [0] 、dp[i] [1] :在第i天持有股票所获得的最大利润为dp[i] [0] ;第 i天不持有股票dp[i] [1] 。所以我们在第i天的时候有持有和不持有股票这两种状态,且持有和不持有并不代表着这一天买或者卖,也可能是前面以及买入或者卖出的情况。

  • 递推公式:

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

      • dp[i-1] [0]:前一天持有股票的最大利润
      • -prices[i] :前一天买入股票,只买一次
    • dp[i-1] [1] = max(dp[i-1] [1], dp[i-1] [0] + prices[i])

      • dp[i-1] [1]:前一天不持有股票的最大利润
      • dp[i-1] [0] + prices[i]:第i天把股票给卖了,所以今日就要赚prices[i],且前一天买入时可能是赊账买入的,所以得加上前一天持有股票的状态
  • 初始化:

    • dp[0] [0]:- prices[0] 买入相当于是钱少了,所以是负数
    • dp[0] [1]:0 不买就是0元
  • 遍历顺序:1<=i<prices.length

  • 打印dp数组

返回结果:

持有股票的最大利润或不持有股票的最大利润:max(dp[i] [0] , dp[i] [1])

其实不持有股票的最大利润肯定是比持有股票的利润大,可直接输出dp[i] [1]

class Solution {
    public int maxProfit(int[] prices) {
        int[][] dp = new int[prices.length][2];
        // 初始化、持有股票和不持有股票两种状态
        dp[0][0] = -prices[0]; // 第0天赊账买入
        dp[0][1] = 0;for (int i = 1; i < prices.length; i++) {
            // 持有股票 => 前一天持有股票或者今天刚好买入(买一次)
            dp[i][0] = Math.max(dp[i-1][0], -prices[i]);
            // 不持有股票 => 前一天不持有股票或者今天刚好卖出(卖一次,前一天持有的利润+卖出的利润)
            dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] + prices[i]);
        }
        // 最后一天不持有股票肯定比持有股票利润多
        return dp[prices.length-1][1];
    }
}

打印结果:

Your input:[7,1,5,3,6,4]
Output:5
Expected:5
stdout:
    dp[i][0] = -1   dp[i][1] = 0
    dp[i][0] = -1   dp[i][1] = 4
    dp[i][0] = -1   dp[i][1] = 4
    dp[i][0] = -1   dp[i][1] = 5
    dp[i][0] = -1   dp[i][1] = 5

总结

本题是股票买卖动态规划的基础,需好好理解持有股票与不持有股票,当天的利润应是如何获得的。

另外本题的dp数组可以压缩成一维的数组:

  • dp数组:

    • dp[0]:持有股票的最大利润
    • dp[1]:不持有股票的最大利润
  • 递推公式:

    • dp[0]:max(dp[0], -prices[i]) 前一天持有或前一天买入
    • dp[1]:max(dp[1], dp[0] + prices[i])前一天不持有或前一天卖出(前一天卖出=前一天持有+利润)
  • 初始化:

    • dp[0] = -prices[0] 第一天持有(相当于赊账买入)
    • dp[1] = 0 第一天不持有
class Solution {
    public int maxProfit(int[] prices) {
        int[] dp = new int[2];
        dp[0] = -prices[0]; // 持有股票
        dp[1] = 0; // 不持有股票
        for (int i = 1; i < prices.length; i++) {
            dp[0] = Math.max(dp[0], -prices[i]);
            dp[1] = Math.max(dp[1], dp[0] + prices[i]);
//            System.out.println("dp[0] =  "+dp[0] + "\tdp[1] = " +dp[1]);
        }
        return dp[1];
    }
}

打印结果:

Your input:[7,1,5,3,6,4]
Output:5
Expected:5
stdout:
    dp[0] =  -1 dp[1] = 0
    dp[0] =  -1 dp[1] = 4
    dp[0] =  -1 dp[1] = 4
    dp[0] =  -1 dp[1] = 5
    dp[0] =  -1 dp[1] = 5

贪心: 另外本题还可以直接使用贪心,只需要找到左边最小的数,最右的最大值,即可求得最大利润

class Solution {
    public int maxProfit(int[] prices) {
        int min = Integer.MAX_VALUE;
        int result = 0;
        for (int i = 0; i < prices.length; i++) {
            // 从左到右找到最小值
            min = Math.min(min, prices[i]);
            // 每次直接比较最大利润区间
            result = Math.max(result, prices[i] - min);
        }
        return result;
    }
}

买卖股票的最佳时机II LeetCode 122

题目链接:买卖股票的最佳时机II LeetCode 122 - 中等

思路

贪心的思路前面就做过了,这里主要考虑使用dp的方法。

和1不一样的地方:可以多次买入和卖出!

  • 不持有股票:前一天不持有,或者前一天刚好卖出
  • 卖出股票:卖股票可以卖多次,所以除了第一次赊账,后面都可以用前一天赚的钱来买股票,也就是前一天不持有股票的状态。
class Solution {
    public int maxProfit(int[] prices) {
        int[][] dp = new int[prices.length][2];
        // 初始化、持有股票和不持有股票两种状态
        dp[0][0] = -prices[0]; // 第0天赊账买入
        dp[0][1] = 0;for (int i = 1; i < prices.length; 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]);
            // System.out.println("dp[i][0] = " + dp[i][0] + "\tdp[i][1] = " + dp[i][1]);
        }
        // 最后一天不持有股票肯定比持有股票利润多
        return dp[prices.length-1][1];
    }
}

总结

同样,本题也可以写成一维数组的形式:

class Solution {
    public int maxProfit(int[] prices) {
        int[] dp = new int[2];
        dp[0] = -prices[0]; // 持有买票,买入(赊账)
        dp[1] = 0; // 不持有股票
        for (int i = 1; i < prices.length; i++) {
            // 持有 昨天持有或今日卖出(后面有钱了就不用赊账)
            dp[0] = Math.max(dp[0], dp[1]-prices[i]);
            // 不持有股票 昨天不持有或今日卖出(今日卖出昨日肯定就持有)
            dp[1] = Math.max(dp[1], dp[0]+prices[i]);
        }
        return dp[1];
    }
}

买卖股票重点:

  1. 今日持有股票的状态:

    1. 昨日不持有,今日才买入 => 就看前面是否赚钱

      • 一次买卖:-prices[i],相当于赊账去买
      • 多次买卖:dp[1]-prices[i],第一次赊账买,后面用前面持有是赚的钱去买
    2. 昨日持有 => dp[0]

  2. 今天不持有股票状态:

    1. 昨日持有,今日卖出 => 都是用昨日持有股票的状态(可能赊账买入),再加上今日卖这张股票赚的钱

      • 一次买卖:dp[0]+prices[i]
      • 多次买卖:dp[0]+prices[i]
    2. 不持有 => dp[1]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值