代码随想录刷题笔记 Day 53 买卖股票的最佳时机 No.121 | 买卖股票的最佳时机 No.122

Day 53

01. 买卖股票的最佳时机(No. 121)

题目链接

代码随想录题解

<1> 题目

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

示例 1:

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

示例 2:

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

提示:

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 104
<2> 笔记

提供一个数组,其中包含每天这支股票的价格,选择一天买入股票,再选择一天卖出股票,问最大能获得的利润是多少。

之前在贪心算法章节做到过的 买卖股票的最佳时机II 中可以买卖多次不同,本题只能进行 一次 买卖。

(1). 贪心思路

本题最容易想出的思路还是贪心思路,也就是选择最便宜的一天买入,最贵的一天卖出:

定义一个 low 来存储卖的最便宜的股票价格,循环遍历数组,查找哪一天卖出最便宜:

class Solution {
    public int maxProfit(int[] prices) {
        int low = Integer.MAX_VALUE;
        int res = 0;
        for (int i : prices) {
            low = Math.min(low, i); // 从 0 - i 最便宜的股票是哪一个
            res = Math.max(res, i - low); // 卖出股票
        }
        return res;
    }
}

很多朋友可能会对 lowres 赋值的顺序有疑惑,题目中不是说不能再某一天同时买入和卖出吗?如果将求最小的放在前面,那就可能会出现同一天买入和卖出的情况。但本题中其实求得是 最大值,如果说是同一天买入和同一天卖出的情况是最优解的话,那说明这个情况是 无法盈利 的,否则后续一定有更大的值可以将其覆盖。

(2). 动态规划思路

与贪心沿着固定的路径进行不同,动态规划其实更偏向于穷举,所以本题的动规解法的时间肯定是要长于贪心的。

首先来明确本题中的 状态 是什么,是在某一天通过买卖股票所持有的金钱,但为了描述这个状态,会出现两种情况:

  1. 当前处于买入的状态,也就是手中还持有股票,持有的金钱为 负数,因为此时只有支出
  2. 当前处于卖出的状态,手中的股票已经被卖出,持有的金钱为 正数

需要注意的是,某一天处于的是 买入 或者 卖出状态,而不是说股票是在这一天买入或者卖出的。

首先来定义 dp 数组,因为要存储某一天的两种状态,所以 dp 数组一定是一个二维数组

  • dp[i][0] 表示当前处于卖出的状态所持有的金钱
  • dp[i][1] 表示当前处于买入的状态所花费的金钱

再来确定递推公式,如果当前处于卖出的状态,说明股票是在今天被卖出,或者说 在昨天就已经处于被卖出的状态,所以:

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

对于当前的买入状态,是要在昨天的买入状态和今天的买入状态取一个最优解,所以:

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

由此可以写出本题的代码。

<3> 代码
class Solution {
    public int maxProfit(int[] prices) {
        int[][] dp = new int[prices.length][2];
        dp[0][0] = 0; // 不持有股票的状态
        dp[0][1] = -prices[0]; // 买入的状态
        for (int i = 1; i < prices.length; i++){
            dp[i][0] = Math.max(dp[i - 1][0], prices[i] + dp[i - 1][1]); // 昨天卖出和今天卖出两种选择
            dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
        }
        return dp[prices.length - 1][0];
    }
}

02. 买卖股票的最佳时机(No. 122)

题目链接

代码随想录题解

<1> 题目

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润

示例 1:

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

示例 2:

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

示例 3:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。

提示:

  • 1 <= prices.length <= 3 * 104
  • 0 <= prices[i] <= 104
<2> 笔记

(1). 回顾贪心解法

本题的贪心解法还是比较简单的,就是先买入一个股票,如果发现降价就直接替换,发现涨价则直接卖出,此时就能保证利润是最多的;感兴趣的话可以看一下我这篇博客
代码随想录刷题笔记 DAY 31 | 买卖股票的最佳时机 II No.122 | 跳跃游戏 No.55 | 跳跃游戏 II No.45

class Solution {
    public int maxProfit(int[] prices) {
        int res = 0;
        int pre = prices[0]; // 记录昨天的金钱
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] > pre) {
                // 说明今天的股价升高了 卖出并且将今天的买入
                res += prices[i] - pre;   
            }
            pre = prices[i];
        }
        return res;
    }
}

(2). 动态规划解法

和上一题不同的是,本题可以买入和卖出多次了,但是 状态 还是不变的,即某一天 持有 或者 不持有 股票获取的最多金钱。

仍然选取和上一题相同的 dp 数组,即 dp[i][0] 表示当天不持有股票获取的最大金额;dp[i][1] 表示当天持有股票的时候的最多金钱。

对于 dp[i][0] 来说和上一题相同,可以选择今天卖出或者之前卖出的最优解,即

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

但对于 dp[i][1] 则不同,因为本次可以买卖多次,所以 dp[i][1]的值不止是可以通过未卖过的状态推导出来,同时也可以通过卖出的状态来推导出来

  • 第一个选择就是不购买今天的股票,也就是 dp[i - 1][1] 即昨天持有股票的情况
  • 第二个选择就是购入今天的股票
    • 可以选择在昨天卖出的情况下购入股票,也就是 dp[i - 1][0] - prices[i]
    • 同时也可以选择在昨天还持有股票的情况下购入股票,即 dp[i - 1][1] - prices[i]

但是对于第 i 天来说 dp[i][0] 是一定 大于等于 dp[i - 1][1] 的,因为 dp[i][0] 在花费最优的基础上还有售出的部分(也可能不售出),所以最终值肯定是大于等于 dp[i][1] 的,所以递推公式从求以上三个公式的最大值变为这样:

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

写出代码

<3> 代码
class Solution {
    public int maxProfit(int[] prices) {
        int[][] dp = new int[prices.length][2];
        dp[0][0] = 0; // 选择卖出的情况
        dp[0][1] = -prices[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]);
        }
        return dp[prices.length - 1][0];
    }
}

1

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

*Soo_Young*

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

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

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

打赏作者

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

抵扣说明:

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

余额充值