浅聊一下
作为一名小白,在leetCode刷题数已经达到了三位数,但是在我刷过的大部分题目中,都是使用的暴力解法,多层循环遍历等等...真的是有受够了自己
今天开始,我决定远离暴力解法,学习一下高端烧脑并且优雅的解法( ఠൠఠ )ノ
LeetCode:买卖股票的最佳时机
我们先来看题目:
给定一个数组
prices
,它的第i
个元素prices[i]
表示一支给定股票第i
天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回
0
。
换做昨天的我估计在想,这这这,简单的不能再简单了,直接双层循环我就给他解决了...
但是今天,我选择用动态规划和贪心来完成这道题
动态规划
我们来看题目最后想要的结果是什么
=>返回你可以从这笔交易中获取的最大利润
注意 最大 这两个字,一般提到最大,我们就要想到动态规划,我们得用动态规划把这题干掉
那动态规划是怎么思考的呢?
-
自顶而下
我们要让最后的结果为最优解,自然要让前面的每一步都是最优解,带入题目
我可以选择今天卖股票,也可以选择昨天卖股票,在今天以前,我们昨天卖掉股票所得的利润是最大的,于是我该和今天比较,如果今天卖的比昨天更多,那么我就今天把股票卖了...
-
状态转移方程
根据我们自顶向下的思想,我们可以列出状态转移方程
dp[i] = Math.max(dp[i-1],prices[i]-min)
dp[i]代表第i天卖出股票获利的最大值,如果第i天卖出的利润比dp[i-1]大,那么我们获得的最大利润就为dp[i],并且我们知道,在第一天买入股票之后当天是不能卖出股票的,所以
dp[0] = 0
js
var maxProfit = function(prices) {
const dp = new Array(prices.length).fill(0)
let min = prices[0]
for(let i = 1;i<prices.length;i++){
min = Math.min(prices[i],min)
dp[i] = Math.max(dp[i-1],prices[i]-min)
}
return dp[prices.length-1]
};
我们使用一个数组 dp
来保存每天卖出股票能够获得的最大利润。数组 dp
的长度与股票价格数组 prices
的长度相同。数组 dp
中的每个元素 dp[i]
表示在第 i
天卖出股票能够获得的最大利润。初始值都为0。
然后,我们从数组的第二个元素开始遍历(i = 1
),对于每个元素 prices[i]
,我们进行以下操作:
- 计算在前
i-1
天中能够获得的最小买入价格min
。我们使用Math.min(prices[i],min)
来更新最小买入价格min
。prices[i]
表示当前的股票价格,如果这个价格比当前的最小买入价格min
小,就更新最小买入价格为这个值。 - 计算在第
i
天卖出股票能够获得的最大利润。我们使用Math.max(dp[i-1], prices[i]-min)
来更新数组dp
中的元素。dp[i-1]
表示在前i-1
天中获得的最大利润,因为我们不能在同一天既买入又卖出股票。所以,在第i
天卖出股票的最大利润为prices[i]-min
,即当前股票价格减去前i-1
天中的最小买入价格。然后,我们使用Math.max
函数来比较dp[i-1]
和prices[i]-min
,取其中的较大值作为dp[i]
的值。
通过遍历完整个数组,我们得到了最大利润。最后,我们返回 dp[prices.length-1]
作为函数的结果。
贪心
我们再来用贪心解决这道题
什么是贪心?就是我很贪心,总是想得到最多的东西...总是做出在当前看来是最好的选择 不从整体最优上加以考虑 算法得到的是局部最优解。
- 在每一个价格上升的时刻,我们选择将股票卖出以获取最大利润;而在价格下降或持平的时候,我们选择保持最低买入价格。通过这样的策略,我们能够在一次遍历中找到最大利润,而不需要进行多次交易。
js
var maxProfit = function(prices) {
let min = prices[0]
let profit = 0
for(let i = 1;i<prices.length;i++){
if(prices[i] > prices[i-1]){
profit = Math.max(prices[i]-min,profit)
}else{
min = Math.min(prices[i],min)
}
}
return profit
};
首先,我们初始化两个变量 min
和 profit
,min
用于记录当前遍历到的最低买入价格,profit
用于记录当前的最大利润,初始值都为0。
然后,我们从数组的第二个元素开始遍历(i = 1
),对于每个元素 prices[i]
,我们进行以下操作:
- 如果当前价格
prices[i]
大于前一个价格prices[i-1]
,说明股票价格上升了。此时我们可以考虑将其卖出,并计算当前的利润。我们使用Math.max(prices[i]-min, profit)
来更新当前的最大利润。prices[i]-min
表示将当前股票卖出后的利润,如果这个利润比当前的最大利润profit
大,就更新最大利润为这个值。 - 如果当前价格
prices[i]
小于等于前一个价格prices[i-1]
,说明股票价格下降了或者持平了。此时我们需要考虑是否要更新最低买入价格min
。我们使用Math.min(prices[i], min)
来更新最低买入价格。prices[i]
表示当前的股票价格,如果这个价格比当前的最低买入价格min
小,就更新最低买入价格为这个值。
通过遍历完整个数组,我们得到了最大利润。最后,我们返回最大利润作为函数的结果。
下班
想当聪明人的第一天完美结束...今天就写到这里吧!对了,最近掘金大大在搞年度优秀创作者投票活动,要是掘友们觉得小弟的文章让您有所收获,就给我投上一票吧!!!