这是我参与更文挑战的第3天,活动详情查看: 更文挑战
原题
给定一个数组 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。
重拳出击
这题依然标签是动态规划,所以还是按照基本套路来。
首先仔细看了看题目,关键是低买高卖,所以这题一定要顺序遍历,并且最底线就是不亏钱,也就是不买股票(确实,不买股票我永远赚)。
-
边界条件
题目写了
prices.length>=1那这个也就没什么特殊的边界了,和前一天的题目贼像,就记一下第一个值prices[i]用来推算就行。 -
子问题
prices是一个长度为i的数组,根据题目低买高卖的意思,也就是以数组的顺序为准,从前往后前面的数字减后面的数字,找到差最大的情况,不能计算绝对值哈。当然有一个条件,就是结果必须大于零。这波把子问题设置为:数组长度为n时候,低买高卖的最大值
然后人脑推算一下,这是一个长度为
n (0 < n << i)的数组,首先需要找到从前往后的最低买入点,即买入的最小值,可以使用一个参数min保留。那么,假如目前dp[n]数组赚的最多的是res,此时的最低买入点为min。当它往下推算一下,数组变为长度为n + 1,这时候res数值就和这个数组的第n个元素有关,需要用这第n个元素去减去前头那个最低买入点然后与现有值去做比较,同时对于最小买入点的替换也要继续进行。这样,就会有两个结果:-
长度为 n 时候就能赚的更多,继续向下比较第
n个元素 -
在第 n 个元素的时候卖出,赚的比前面更多,记录
dp[n],然后继续
上头这个结论是能够一直往后面计算的,直到
n = i。最终遍历dp[n]找到最大值就好 -
-
方程
根据上一条,可以得到如下:
当数组
prices[i]遍历结束之后,会得到一个dp[n]数组中存的是长度为n时候,高卖低买能赚到的最多的钱。 -
结合各种条件写代码
public int maxProfit(int[] prices) {
//题目的边界条件
if (prices.length == 1) {
return 0;
}
//最小买入点比较,记住要初始化理论最大值
int min = Integer.MAX_VALUE;
int[] dp = new int[prices.length+1];
//初始值
dp[0] = 0;
for (int i = 1; i < prices.length; i++) {
//循环替换能找到的最低买入点
min = Math.min(min, prices[i-1]);
//比较那种情况得到的差更大
dp[i] = Math.max(dp[i], prices[i] - min);
}
//题目要求最少赚0元,不然应该理论最小值Integer.MIN_VALUE
int res = 0;
//遍历找到最赚的情况
for (int i : dp) {
if (res < i){
res = i;
}
}
return res;
}
然后运行,发现 执行耗时击败了19.44%,内存击败了6.19%
太拉跨了,这个情况可以空间优化,因为只需要一边迭代,一边判断差最大就行了。
改一改:
public int maxProfit(int[] prices) {
if (prices.length == 1) {
return 0;
}
int min = Integer.MAX_VALUE;
int dp = 0;
int res = 0;
for (int i = 1; i < prices.length; i++) {
min = Math.min(min, prices[i-1]);
dp = Math.max(dp, prices[i] - min);
if (dp > res) {
res = dp;
}
}
return res;
}
然后运行,发现 执行耗时击败了60.90%,内存击败了56.83%
效果还行吧,再往下,暂时没想到,日后在搞,毕竟我只会简单技巧的样子。
收
-
动态规划的空间优化还是很有必要的