代码随想录算法训练营第四十六天| LeetCode121. 买卖股票的最佳时机、LeetCode122.买卖股票的最佳时机II、LeetCode123.买卖股票的最佳时机III

#LeetCode121. Best Time to Buy and Sell Stock

#LeetCode121. 视频讲解:动态规划之 LeetCode:121.买卖股票的最佳时机1_哔哩哔哩_bilibili

动态规划五部曲:

1. dp数组的含义:dp[i][0] 表示第i 支股票持有时的最大金额,dp[i][1] 表示第i 支股票不持有时的最大金额。这个持有与否是代表的一种状态而不是一定在这个时候买入或卖出。如果在第2 支股票时买入,那么在第3、4、5都是持有的状态

2. 递推公式:如果是持有股票的状态,有可能是之前已经买入,那么就是dp[i - 1][0] ,如果是这次买入则为-prices[i] ,如果是当前不持有股票,有可能是之前已经卖出,那么就是dp[i - 1][1],或者这次卖出dp[i - 1][0] + prices[i] ,是当前的价格加上之前持有股票的最大现金

3. dp数组初始化:dp[i][0] = -prices[i] 是买入第一支股票的金额,是负值,因为起始的金额为0, dp[0][1] = 0 是不持有股票的状态,则为0

4. 遍历顺序:正序遍历

5. 打印dp 数组:打印方便理解,如果把例子可视化可以看出二维数组是为了记录买入的最低价格以及记录卖出的最高价格:

例子[7, 1, 5, 3, 6, 4]

dp[0][0] = -7; dp[0][1] = 0;

dp[1][0] = max(-7, -1) = -1; dp[1][1] = max(0, -7 + 1) = 0;

dp[2][0] = max(-1, -5) = -1; dp[2][1] = max(0, -1 + 5) = 4;

dp[3][0] = max(-1, -3) = -1; dp[3][1] = max(4, -1 + 3) = 4;

dp[4][0] = max(-1, -6) = -1; dp[4][1] = max(4, -1 + 6) = 5;

dp[5][0] = max(-1, -4) = -1; dp[5][1] = max(5, -1 + 4) = 5;

代码:

class Solution {
    public int maxProfit(int[] prices) {
        int[][] dp = new int[prices.length][2];
        dp[0][0] = -prices[0]; // have
        dp[0][1] = 0; // have not
        for (int i = 1; i < prices.length; i++) {
            dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
        }
        return dp[prices.length - 1][1];
    }
}

#LeetCode122. Best Time to Buy and Sell Stock II

#LeetCode122. 视频讲解:动态规划,股票问题第二弹 | LeetCode:122.买卖股票的最佳时机II_哔哩哔哩_bilibili

与第一个股票买卖问题的区别是,之前只允许买卖一次,这个题目是允许多次买卖的。

如果使用贪心算法,那么只需要考虑只要第二天比前一天价格高就卖出。

在动态规划算法中,卖出的情况是相同的,但买入的情况不同。区别在于之前是只有一次机会买入卖出,所以价格是0 - prices[i],现在有了之前买入卖出赚的差额,不再是之前的0 了,所以dp[i][0] 有不同。

动态规划代码:

class Solution {
    public int maxProfit(int[] prices) {
        // Dynamic Programming
        int[][] dp = new int[prices.length][2];
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        for (int i = 1; i < prices.length; 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[prices.length - 1][1];
    }
}

#LeetCode123. Best Time to Buy and Sell Stock III

#LeetCode122. 视频讲解:动态规划,股票至多买卖两次,怎么求? | LeetCode:123.买卖股票最佳时机III_哔哩哔哩_bilibili

与前两题不同的是,这个题目只允许买卖两次,可以创建一个更大的数组来分别记录不同状态的金额。

动态规划五部曲:

1. dp数组的含义:dp[i][0] 表示最初没有持有过股票时的状态,dp[i][1] 表示第i 支股票持有时(第一次持有)的最大金额,dp[i][2] 表示第i 支股票不持有时(第一次不持有)的最大金额,dp[i][3] 表示第i 支股票持有时(第二次持有)的最大金额,dp[i][4] 表示第i 支股票不持有时(第二次不持有)的最大金额

2. 递推公式:

第一次持有dp[i][1] :有可能是之前已经买入,那么就是dp[i - 1][1] ,如果是这天刚买入则为dp[i - 1][0] - prices[i] (其实就是-prices[i] )

第一次不持有dp[i][2] :有可能是之前已经卖出,那么就是dp[i - 1][2] ,如果是这天刚卖出则为dp[i - 1][1] + prices[i]

第二次持有dp[i][3] :有可能是之前已经买入,那么就是dp[i - 1][3] ,如果是这天刚买入则为dp[i - 1][2] - prices[i] (dp[i - 1][2] 代表第一次卖出后结余的钱)

第二次不持有dp[i][4] :有可能是之前已经卖出,那么就是dp[i - 1][4] ,如果是这天刚卖出则为dp[i - 1][3] + prices[i]

3. dp数组初始化:dp[0][0] 代表没有购入股票的初始状态,是初始金额0 ;dp[0][1] 是代表第一天买入 = -prices[0] ;dp[0][2] 是代表第一天买入又卖出 = 0 ;dp[0][3] 代表第一天连续买入卖出买入 = -prices[0] ;dp[0][4] 是代表第一天买入卖出又买入卖出 = 0

4. 遍历顺序:正序遍历,因为dp[i] 依赖于dp[i - 1] 的状态

5. 打印dp 数组:打印方便理解

最后的结果可以不取最大值,因为第二次的当天买入卖出也是可以的(收支平衡),依然是最后的数组代表最大金额。

代码:

class Solution {
    public int maxProfit(int[] prices) {
        int[][] dp = new int[prices.length][5];
        dp[0][0] = 0;
        dp[0][1] = -prices[0]; // buy
        dp[0][2] = 0; // buy and sell
        dp[0][3] = -prices[0]; // buy
        dp[0][4] = 0; // buy and sell
        for (int i = 1; i < prices.length; i++) {
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
            dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
            dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
            dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
        }
        return dp[prices.length - 1][4];
    }
}
第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值