1. 题目描述
[121] 买卖股票的最佳时机
给定一个数组 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
Related Topics 数组 动态规划
2. 思路分析
理解题目,主要的想法还是在遍历的过程中,找到一个最小值,也就是最便宜的价格,然后再后面的天数卖出去。
我的想法是再边遍历的时候边计算,如果值小于当前记录的最便宜的值,那说明更适合买入。如果不适合买入,那试试卖出的价格是不是比之前最大的卖出值更大,如果是,说明这就是一个更优的买卖方式。(method1、method3)
method2比较符合动态规划的做法,详细看注释
3. AC代码
public class No121 {
public int maxProfit(int[] prices) {
return method1(prices);
// return method2(prices);
// return method3(prices);
}
/**
* 理解同method1
*
* https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/mai-mai-gu-piao-de-zui-jia-shi-ji-bao-li-yywe/
* 执行耗时:3 ms,击败了47.07% 的Java用户
* 内存消耗:51.2 MB,击败了50.44% 的Java用户
* @param prices
* @return
*/
private int method3(int[] prices) {
int n = prices.length;
if (n < 2) return 0;
int min = Integer.MAX_VALUE;
int result = 0;
// 时间比method1 多,应该因为每次循环都需要判断两次
for (int i=0;i<n;i++) {
//从左边开始找到最低价格
min = Math.min(min, prices[i]);
//第i天的价格与在这之前的最低价进行做差,最终得到最大利润
result = Math.max(result, prices[i] - min);
}
return result;
}
/**
* 更像是用了动态规划,不过感觉效果没有method1直观
*
* https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/121-mai-mai-gu-piao-de-zui-jia-shi-ji-zu-1v0i/
* 执行耗时:4 ms,击败了27.47% 的Java用户
* 内存消耗:51.4 MB,击败了38.89% 的Java用户
* @param prices
* @return
*/
private int method2(int[] prices) {
int dp0 = 0; // 一直不买
int dp1 = - prices[0]; // 只买了一次
int dp2 = Integer.MIN_VALUE; // 买了一次,卖了一次
for(int i = 1; i < prices.length; i++){
// 价格更优惠,买
dp1 = Math.max(dp1, dp0 - prices[i]);
// dp1 是负值,加上当前值的数,比较大说明卖出价格划算
dp2 = Math.max(dp2, dp1 + prices[i]);
}
// dp2还有可能是 负值,负值不如不买,选择dp0
return Math.max(dp0, dp2);
}
/**
* 初始值为0,因为有可能不交易
*
* 内存消耗:51.1 MB,击败了54.15% 的Java用户
* 内存消耗:38.4 MB,击败了61.17% 的Java用户
* @param prices
* @return
*/
private int method1(int[] prices) {
// 初始化值
int maxEarnPrice = 0;
int cheapestPrice = prices[0];
for (int i = 0; i < prices.length; i++) {
// 如果有更小的值,按说明更适合购买
if (cheapestPrice > prices[i]) {
cheapestPrice = prices[i];
} else {
int earnPrice = prices[i] - cheapestPrice;
// 如果当前遍历到的值比已记录的最大收益额更大,那说明更适合卖出
maxEarnPrice = Math.max(earnPrice,maxEarnPrice);
}
}
return maxEarnPrice;
}
}
4. 总结
结合leetcode - [53]最大子序和 ,感觉有那么一丝丝理解动态规划,不知道大家有没有感觉(可能也是题目简单)。不过,最重要还是多多联系,冲冲冲
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情