LeetCode 股票买卖系列

点击去我的博客看看吧>-<

欢迎指正

参考来源: labuladong的算法小抄

墙裂推荐上面那个大佬的算法文章。

基础状态转移方程:(根据需要进行修改)

base case:
dp[-1][k][0] = dp[i][0][0] = 0
dp[-1][k][1] = dp[i][0][1] = -infinity

// 状态转移方程:
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
// 之所以 k - 1,是因为,之前以及买入过了,所以买入的次数就要-1
// 理解一下就是:一次交易由(买-卖)构成,k次交易就有 (k次买-k次卖)有点类似于左右括号    
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
    
// dp[3][2][1] 的含义就是:今天是第三天,我现在手上持有着股票,至今最多进行 2 次交易
// dp[3][2][0] 的含义就是:今天是第三天,我现在手上没持有着股票,至今最多进行 2 次交易

// 我们要求的就是 dp[n - 1][k][0]:今天是最后一天,至今最多进行2次交易,我的最大获利

说明:下面的优化之所以能够进行,是因为在这几个题目中,状态转移只在相邻的两个状态间发生,所以可以使用一个变量存储前后两个状态值,然后进行更新就可以了。

121. 买卖股票的最佳时机

1. 解法一:动态规划(带哈希表)

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length <= 1) return 0;
        int n = prices.length;
        int[][] dp = new int[n][2];
        for (int i = 0;i < n;i ++) {
            // base contidition
            if (i - 1 == -1) {
                // 第 0 天不操作
                dp[i][0] = 0;
                // 第 0 天买入
                dp[i][1] = -prices[i];
                continue;
            }
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            //                   因为交易次数只能1次,前面已经买过了,所以不能再买
            dp[i][1] = Math.max(dp[i - 1][1], - prices[i]);
        }
        // 最后一天不持有的利润最大
        return dp[n - 1][0];
    }
}

2. 解法二:优化空间复杂度到 O(1)

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length <= 1) return 0;
        int n = prices.length;
        int dp_i_0 = 0, dp_i_1 = -prices[0];
        for (int i = 1;i < n;i ++) {
            dp_i_0 = Math.max(dp_i_0, dp_i_1 + prices[i]);
            dp_i_1 = Math.max(dp_i_1, -prices[i]);
        }
        return dp_i_0;
    }
}

122. 买卖股票的最佳时机 II

不限制交易次数,即 k = infinity,则 k - 1 = k

1. 解法一:动态规划(带哈希表)

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length <= 1) return 0;
        int n = prices.length;
        int[][] dp = new int[n][2];
        for (int i = 0;i < n;i ++) {
            // base condition
            if (i == 0) {
                dp[i][0] = 0;
                dp[i][1] = -prices[i];
                continue;
            }
            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];
    }
}

2. 解法二:优化空间复杂度到 O(1)

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length <= 1) return 0;
        int n = prices.length;
        int dp_i_0 = 0, dp_i_1 = -prices[0];
        for (int i = 1;i < n;i ++) {
            // 因为 dp_i_1 需要的是更新前的 dp_i_0,所以需要先保存起来
            int temp = dp_i_0;
            dp_i_0 = Math.max(dp_i_0, dp_i_1 + prices[i]);
            dp_i_1 = Math.max(dp_i_1, temp - prices[i]);
        }
        return dp_i_0;
    }
}

123. 买卖股票的最佳时机 III

原始的动态转移方程,没有可化简的地方
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])

1. 解法一:动态规划(带哈希表)

因为不只与前一个状态相关,所以想优化空间复杂度不好实现(我不会了)

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length <= 1) return 0;
        int n = prices.length;
        // 最多完成 2 笔交易
        int max_k = 2;
        int[][][] dp = new int[n][max_k + 1][2];
        for (int i = 0;i < n;i ++) {
            // 因为要考虑到最多交易数为2,所以需要遍历一遍
            for (int k = max_k; k >= 1;k --) {
                // base condition
                if (i == 0) {
                    dp[i][k][0] = 0;
                    dp[i][k][1] = -prices[i];
                    continue;
                }
                dp[i][k][0] = Math.max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
                dp[i][k][1] = Math.max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]);
            }
        }
        return dp[n - 1][max_k][0];
    }
}

188. 买卖股票的最佳时机 IV

1. 解法一:动态规划(带哈希表)——> 超出内存限制

class Solution {
    public int maxProfit(int k, int[] prices) {
        if (prices == null || prices.length <= 1) return 0;
        int n = prices.length;
        int[][][] dp = new int[n][k + 1][2];
        for (int i = 0;i < n;i ++) {
            for (int j = k;j >= 1;j --) {
                // base condition
                if (i == 0) {
                    dp[i][j][0] = 0;
                    dp[i][j][1] = -prices[i];
                    continue;
                }
                dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
                dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
            }
        }
        return dp[n - 1][k][0];
    }
}

2. 解法二:进行优化

一次交易由买入和卖出构成,至少需要两天。所以说有效的限制 k 应该不超过 n/2,如果超过,就没有约束作用了,相当于 k = +infinity。这种情况见 122

class Solution {
    public int maxProfit(int k, int[] prices) {
        if (prices == null || prices.length <= 1) return 0;
        int n = prices.length;
        if (k > n / 2) {
            return maxProfit_infinity(prices);
        }
        int[][][] dp = new int[n][k + 1][2];
        for (int i = 0;i < n;i ++) {
            for (int j = k;j >= 1;j --) {
                // base condition
                if (i == 0) {
                    dp[i][j][0] = 0;
                    dp[i][j][1] = -prices[i];
                    continue;
                }
                dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
                dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
            }
        }
        return dp[n - 1][k][0];
    }
	// 这一部分的代码,其实就是复用了 122 的代码
    private int maxProfit_infinity(int[] prices) {
        int n = prices.length;
        int[][] dp = new int[n][2];
        for (int i = 0;i < n;i ++) {
            if (i == 0) {
                dp[i][0] = 0;
                dp[i][1] = -prices[i];
                continue;
            }
            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];
    }
}

309. 最佳买卖股票时机含冷冻期

1. 解法一:动态规划(带哈希表)

注意:因为带来冷冻期,所以状态转移方程有了一定变化

dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
// 解释:第 i 天选择 buy 的时候,要从 i-2 的状态转移,而不是 i-1 。中间经历一个冷冻期 i - 1
dp[i][1] = max(dp[i-1][1], dp[i-2][0] - prices[i])
class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length <= 1) return 0;
        int n = prices.length;
        // 多次买卖,所以 k = infinity
        int[][] dp = new int[n][2];
        for (int i = 0;i < n;i ++) {
            // base condition
            if (i == 0) {
                dp[i][0] = 0;
                dp[i][1] = -prices[i];
                continue;
            }
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            // 要考虑从 i - 2 开始转移,所以需要提前判断 i 和 2 的大小关系
            if (i == 1) {
                dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
            } else
                dp[i][1] = Math.max(dp[i - 1][1], dp[i - 2][0] - prices[i]);
        }
        return dp[n - 1][0];
    }
}

2. 解法二:优化空间复杂度到 O(1)

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length <= 1) return 0;
        int n = prices.length;
        int dp_i_0 = 0;
        int dp_i_1 = -prices[0];
        // 记前两天不持有的利润即:dp[i - 2][0]
        int dp_pre_0 = 0;
        for (int i = 1;i < n;i ++) {
            int temp = dp_i_0;
            dp_i_0 = Math.max(dp_i_0, dp_i_1 + prices[i]);
            dp_i_1 = Math.max(dp_i_1, dp_pre_0 - prices[i]);
            // 往后遍历的时候,需要时刻更新这个 dp_pre_0,即dp[i - 2][0]
            dp_pre_0 = temp;
        }
        return dp_i_0;
    }
}

714. 买卖股票的最佳时机含手续费

本质上来说与122 无限购买次数相同,我们只需要在卖出的同时扣除手续费即可

1. 解法一:动态规划(带哈希表)

class Solution {
    public int maxProfit(int[] prices, int fee) {
        if (prices == null || prices.length <= 1) return 0;
        int n = prices.length;
        int[][] dp = new int[n][2];
        for (int i = 0;i < n;i ++) {
            if (i == 0) {
                dp[i][0] = 0;
                dp[i][1] = -prices[i];
                continue;
            }
            // 在卖出的时候扣除手续费即可,在买入的时候扣除也是可以的
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee);
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
        }
        return dp[n - 1][0];
    }
}

2. 解法二:优化空间复杂度到 O(1)

class Solution {
    public int maxProfit(int[] prices, int fee) {
        if (prices == null || prices.length <= 1) return 0;
        int n = prices.length;
        int dp_i_0 = 0;
        int dp_i_1 = -prices[0];
        for (int i = 1;i < n;i ++) {
            int temp = dp_i_0;
            dp_i_0 = Math.max(dp_i_0, dp_i_1 + prices[i] - fee);
            dp_i_1 = Math.max(dp_i_1, temp - prices[i]);
        }
        return dp_i_0;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值