layout: posttitle: LeetCode 从滑动窗口到初级动态规划解决一类题
subtitle: LeetCode 从滑动窗口到初级动态规划解决一类题
date: 2020-12-23
author: he xiaodong
header-img: img/default-post-bg.jpg
catalog: true
tags:
- Go
- PHP
- LeetCode 01.05
- 字符串比较

剑指 Offer 57 - II. 和为s的连续正数序列

输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。

序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

示例 1:

  1. 输入:target = 9
  2. 输出:[[2,3,4],[4,5]]

示例 2:

  1. 输入:target = 15
  2. 输出:[[1,2,3,4,5],[4,5,6],[7,8]]

限制:

1 <= target <= 10^5

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof

解题思路

经典滑动窗口,直接限制最多滑到一半,能省很多计算。

代码实现:

  1. class Solution {
  2. /**
  3. * @param Integer $target
  4. * @return Integer[][]
  5. */
  6. function findContinuousSequence($target) {
  7. $i = 1;
  8. $j = 2;
  9. $result = [];
  10. # 滑动窗口的右边界不能超过 target 的中值
  11. while ($j <= $target / 2 + 1) {
  12. # 计算当前窗口内数字之和
  13. $curSum = array_sum(range($i, $j + 1));
  14. # 若和小于目标,右指针向右移动,扩大窗口
  15. if ($curSum < $target) {
  16. $j++;
  17. # 若和大于目标,左指针向右移动,减小窗口
  18. } elseif ($curSum > $target) {
  19. $i++;
  20. }
  21. # 相等就把指针形成的窗口添加进输出列表中
  22. # 别忘了,这里还要继续扩大寻找下一个可能的窗口
  23. else {
  24. $result[] = range($i, $j + 1);
  25. # 这里用j+=1,i+=1,i+=2都可以的
  26. $j++;
  27. }
  28. }
  29. return $result;
  30. }
  31. }

剑指 Offer 42. 连续子数组的最大和

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

要求时间复杂度为O(n)。

示例1:

  1. 输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
  2. 输出: 6
  3. 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof

解题思路 1

继续滑动窗口,比当前最大值大则右移指针,比当前最大值小则左指针开始收缩。

代码实现:

  1. class Solution {
  2. /**
  3. * @param Integer[] $nums
  4. * @return Integer
  5. */
  6. function maxSubArray($nums) {
  7. //sum 用于记录滑动窗口中所有数的和
  8. $sum = 0; $left = 0; $result = PHP_INT_MIN;
  9. $count = count($nums);
  10. for ($right = 0; $right < $count; $right++) {
  11. $sum += $nums[$right];
  12. //每次右指针移动一格,就需要比较一次
  13. $result = max($result, $sum);
  14. //注意这里当 left==right 时,left 指针也要移
  15. while ($left <= $right && $sum <= 0) {
  16. $sum -= $nums[$left];
  17. $left++;
  18. }
  19. }
  20. return $result;
  21. }
  22. }

解题思路2 动态规划

动态规划解法:
首先定义看给出的大问题是否能拆分成小问题,最直接的就是一些数组中求性质,可以看成一个个更小的数组所构成的小问题。这些小问题构成了了一个个小状态,要想清楚怎么从这些小状态走到大状态,进而解决大问题(这就是状态转移方程)。核心是:
○ 如何抽象,定义状态dp[i]
○ 状态转移方程的确定 dp[i]如何从之前的状态得到
○ 初始的一些状态是可以看出来的比如dp[0],dp[1]

代码实现:

  1. class Solution {
  2. /**
  3. * @param Integer[] $nums
  4. * @return Integer
  5. */
  6. function maxSubArray($nums) {
  7. $count = count($nums);
  8. if ($count === 0) {
  9. return 0;
  10. }
  11. $dp = $nums[0];
  12. $max = $nums[0];
  13. for ($i = 1;$i < $count; $i++) {
  14. $dp = max($dp + $nums[$i], $nums[$i]);
  15. $max = max($max, $dp);
  16. }
  17. return $max;
  18. }
  19. }

上题可以引申出:买卖股票的最佳时机 Ⅰ: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/ 和上面的类似,股价变化 [7,1,5,3,6,4] 可以转化成 [-6, 4, -2, 3, -2],买入和卖出一次,可以转换成求连续子数组的最大和

代入一个 多个子序列最大和 的问题,例如 给定一个整数数组 nums ,找到一个或多个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和,若最大和小于0,则返回 0
○ 示例:[-2,1,-3,4,-1,2,1,-5,4]
○ 输出:12
○ 解释:连续子数组 [1],[4],[2,1],[4] 的和最大,为 12。
实际上是这道题是从数组中取所有的正数,累加的结果就是最大和

继续引申出另一个类似问题:

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

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

  1. 输入: [7,1,5,3,6,4]
  2. 输出: 7
  3. 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4
  4. 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3

示例 2:

  1. 输入: [1,2,3,4,5]
  2. 输出: 4
  3. 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4
  4. 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
  5. 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

  1. 输入: [7,6,4,3,1]
  2. 输出: 0
  3. 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii

代码实现:

  1. class Solution {
  2. /**
  3. * @param Integer[] $prices
  4. * @return Integer
  5. */
  6. function maxProfit($prices) {
  7. $ans = 0;
  8. $count = count($prices);
  9. for ($i = 1; $i < $count; $i++) {
  10. if ($prices[$i] - $prices[$i-1] > 0) {
  11. $ans += $prices[$i] - $prices[$i - 1];
  12. }
  13. }
  14. return $ans;
  15. }
  16. }

参考链接:

  1. 极客时间 算法面试通关40讲

最后恰饭 阿里云全系列产品/短信包特惠购买 中小企业上云最佳选择 阿里云内部优惠券