1. 动态规划
解题思路:时间复杂度O(
n
n
n ),空间复杂度O(
n
n
n )
DP数组及下标含义
我们要求出的是
某一天的最大收益。显然dp数组中存储的是
某一天的最大收益。要求出谁的
?显然是求出,某一天,持有股票的收益,和不持有股票的收益。那么下标就是代表是哪一天,有股票,还是没有股票
。很显然,需要两个下标,二维数组。一个下标代表是第几天,另一个下标代表这天是否持有股票
递推公式:从零开始计算
第0天,若没有买股票,那么我们利润为0,若我们买了这天的股票,那么就花了购买这支股票的钱,利润为-prices[0]. 也就是说dp[0][0] = 0表示第0天,不持有股票,利润为0.dp[0][1] = -prices[0],表示第0天,持有股票,利润为-prices[0] 之后每一天,都需要考虑前一天的状况 若今天没有持有股票,dp[i][0]有两种情况:
昨天也没有股票,那么昨天利润是多少,今天还是多少,dp[i][0] = dp[i-1][0] 昨天有股票,那么说明今天把股票卖了,会收获卖掉的钱,也就是dp[i-1][1] + prices[i] 我们要保存的结果是第i天不持有股票的最大利润,也就是两种情况种,选利润最大的。所以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] 昨天没有股票,今天买入这支股票,则需要减去prices[i]的开销。dp[i][1] = dp[i-1][0] - prices[i] 同样选大的,dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
最后一天必须不持有股票,所以最终结果为dp[end][0].
普通版本
class Solution {
public int maxProfit ( int [ ] prices) {
int n = prices. length;
int [ ] [ ] dp = new int [ n] [ 2 ] ;
dp[ 0 ] [ 0 ] = 0 ;
dp[ 0 ] [ 1 ] = - prices[ 0 ] ;
for ( int i = 1 ; i < n; ++ 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[ n - 1 ] [ 0 ] ;
}
}
优化版本:我们发现dp[i]只需要dp[i-1]的状态,所以通过两个变量保存上次的状态即可,用不着dp数组
class Solution {
public int maxProfit ( int [ ] prices) {
int n = prices. length;
int dp0 = 0 , dp1 = - prices[ 0 ] ;
for ( int i = 1 ; i < n; ++ i) {
int newDp0 = Math . max ( dp0, dp1 + prices[ i] ) ;
int newDp1 = Math . max ( dp1, dp0 - prices[ i] ) ;
dp0 = newDp0;
dp1 = newDp1;
}
return dp0;
}
}
2. 贪心解法2
解题思路:时间复杂度O(
n
n
n ),空间复杂度O(
1
1
1 )
计算相邻两天的差值,如果是正数,就收获利润,如果是负数,就抛弃 [7,1,5,10,3,6,4]为例,用第2个-第一个,第3个-第二个,依次类推 两两差值为[-6,4,5,-7,3,-2].我们收获4,5和3
4代表,第二天买入1,第3天卖出5,利润5-1=4 5代表,第3天买入5,第4天卖出10,利润为5.
但是此时的情况是,我们第2天买入1,第3天卖了5,何来第3天买入5,第4天卖出10呢? 此时想象我们可以穿越时空,我们穿越回第三天,让它不要卖 最后我们相当于第二天买入1,第4天卖出10,共收获利润9 同时我们发现第二天买入1,第3天卖出5,利润5-1=4 加上 第3天买入5,第4天卖出10,利润为5。总共赚得利润也是9.(可以这样理解,我们第三天卖了后,赚了4元,但是第三天卖我反悔了,我要第4天卖,第4天卖多赚5元,那么把5元给我们补上。也就是4+5 = 9元
)
3代表第5天买入3,第6天卖出6,赚利润6-3=3 最终利润为9+3 = 12.
class Solution {
public int maxProfit ( int [ ] prices) {
int ans = 0 ;
int n = prices. length;
for ( int i = 1 ; i < n; ++ i) {
ans += Math . max ( 0 , prices[ i] - prices[ i - 1 ] ) ;
}
return ans;
}
}
3. 贪心解法2:超越100%,没错就是我在内卷你们
这个思路之所以快,是因为它省略了Math.max()这个比较的方法,提升了一倍的效率
解题思路:时间复杂度O(
n
n
n ),空间复杂度O(
1
1
1 )
遇到最低点,就买进 遇到高点,就假设自己卖出,然后将这个高点再次当成最低点,回到第一步判断 这样就拿到了最大利润
[1,2,3,4,5]为例 第一天为目前最低点,为1,假设我们买进 第二天为目前高点,为2,假设我们卖出,此时获利为1。设置2为目前最低点。因为我们是假设卖出,未来如果有更高的价钱moreMoney,那么我们再假设是这次更高的时候卖出,则利润,就多出了moreMoney-2这么多。例如第三天的情况 第三天为目前更高点,为3,假设我们这天卖出,也就是我们反悔了,不在第二天卖出了,那么今天卖比昨天多卖1元(3-2 = 1),所以我们利润+1,此时获利为2 第4天为新的最高的,为4,假设这天卖出,我们又返回了,也不再第3天卖出了,那么今天卖比第三天卖多卖1元(4-3 = 1),所以利润+1,此时获利为3 第5天,为新高,为5,再次返回,今天卖又能多赚一元(5-4 = 1),利润+1,为4
[7,1,5,3,6,4]为例 第一天遇到最低点买进,此时利润为-7 第二天遇到新的最低点,我们反悔在第一天买进,而选择今天买进,则利润更新为-1 第三天为高点,假设卖出,获利(5-1 = 4)元,设置5为新的低点 第四天
,遇到更低点3,假设我们此时选择买进。因为比第3天低。这时最好的情况时,第三天真的卖出,然后第4天看情况买进,如果未来有更高的,肯定是这样赚的更多(最低也是赚的一样,不信看下面的例子)
例如我们修改第一个例子[1,2,3,4,5]为[1,3,2,4,5], 此时第一天买进,第5天卖获利4元。第一天买,第二天卖,获利2元,然后第3天买,第5天卖,获利3元,3+2 = 5元。
第5天,遇到高点6,假设卖出,获利(6-3 = 3)元,设置6为新的最低点。因为此时好的选择是,第四天真的买进,因为今天遇到了高点,第四天买进,今天肯定赚。但是也只能假设卖出,因为后面没准有更高点
第6天,遇到新的低点4,假设买进。发现后面没有了,那么这个4,更好的选择就是不买 最终,利润为4+3 = 7
class Solution {
public int maxProfit ( int [ ] prices) {
int min = Integer . MAX_VALUE, pro = 0 ;
for ( int i = 0 ; i < prices. length; i++ ) {
if ( prices[ i] < min) {
min = prices[ i] ;
} else if ( prices[ i] > min) {
pro += prices[ i] - min;
min = prices[ i] ;
}
}
return pro;
}
}