java数据结构与算法刷题-----LeetCode122. 买卖股票的最佳时机 II

java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.youkuaiyun.com/grd_java/article/details/123063846

在这里插入图片描述

1. 动态规划

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

例如我们修改第一个例子[1,2,3,4,5]为[1,3,2,4,5], 此时第一天买进,第5天卖获利4元。第一天买,第二天卖,获利2元,然后第3天买,第5天卖,获利3元,3+2 = 5元。

  1. 第5天,遇到高点6,假设卖出,获利(6-3 = 3)元,设置6为新的最低点。因为此时好的选择是,第四天真的买进,因为今天遇到了高点,第四天买进,今天肯定赚。但是也只能假设卖出,因为后面没准有更高点
  2. 第6天,遇到新的低点4,假设买进。发现后面没有了,那么这个4,更好的选择就是不买
  3. 最终,利润为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) {//如果今天的股票比min大
                pro += prices[i] - min;//假设卖掉这个股票,将利润加到pro
                min = prices[i];//将当前值,设置为新的最低点
            }
        }
        return pro;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ydenergy_殷志鹏

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

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

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

打赏作者

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

抵扣说明:

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

余额充值