|
| 1 | + |
| 2 | +## 题目地址(918. 环形子数组的最大和) |
| 3 | + |
| 4 | +https://leetcode.cn/problems/maximum-sum-circular-subarray/ |
| 5 | + |
| 6 | +## 题目描述 |
| 7 | + |
| 8 | +``` |
| 9 | +给定一个长度为 n 的环形整数数组 nums ,返回 nums 的非空 子数组 的最大可能和 。 |
| 10 | +
|
| 11 | +环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i] 的下一个元素是 nums[(i + 1) % n] , nums[i] 的前一个元素是 nums[(i - 1 + n) % n] 。 |
| 12 | +
|
| 13 | +子数组 最多只能包含固定缓冲区 nums 中的每个元素一次。形式上,对于子数组 nums[i], nums[i + 1], ..., nums[j] ,不存在 i <= k1, k2 <= j 其中 k1 % n == k2 % n 。 |
| 14 | +
|
| 15 | + |
| 16 | +
|
| 17 | +示例 1: |
| 18 | +
|
| 19 | +输入:nums = [1,-2,3,-2] |
| 20 | +输出:3 |
| 21 | +解释:从子数组 [3] 得到最大和 3 |
| 22 | +
|
| 23 | +
|
| 24 | +示例 2: |
| 25 | +
|
| 26 | +输入:nums = [5,-3,5] |
| 27 | +输出:10 |
| 28 | +解释:从子数组 [5,5] 得到最大和 5 + 5 = 10 |
| 29 | +
|
| 30 | +
|
| 31 | +示例 3: |
| 32 | +
|
| 33 | +输入:nums = [3,-2,2,-3] |
| 34 | +输出:3 |
| 35 | +解释:从子数组 [3] 和 [3,-2,2] 都可以得到最大和 3 |
| 36 | +
|
| 37 | +
|
| 38 | + |
| 39 | +
|
| 40 | +提示: |
| 41 | +
|
| 42 | +n == nums.length |
| 43 | +1 <= n <= 3 * 10^4 |
| 44 | +-3 * 104 <= nums[i] <= 3 * 10^4 |
| 45 | +``` |
| 46 | + |
| 47 | +## 前置知识 |
| 48 | + |
| 49 | +- 动态规划 |
| 50 | + |
| 51 | +## 公司 |
| 52 | + |
| 53 | +- 暂无 |
| 54 | + |
| 55 | +## 思路 |
| 56 | + |
| 57 | +数据范围是 10 ^ 4 意味着暴力的 n ^ 2 是不能接受的。 |
| 58 | + |
| 59 | +如果不考虑环这个条件,那么这是一道经典的子序和问题。对于子序和不熟悉的同学,可以看下我之前的博文:https://lucifer.ren/blog/2020/06/20/LSS/ |
| 60 | + |
| 61 | +简单来说,如果是不考虑环的子序和,我们可以定义 dp[i] 为以 nums[i] 结尾的最大子序和,那么答案就是 max(dp)。 |
| 62 | + |
| 63 | +那么对于 nums[i] 来说, 其可以和 nums[i-1] 结合形成子序列,也可以自立门户以 nums[i] 开头形成子序列。 |
| 64 | + |
| 65 | +1. 和 nums[i-1] 结合形成子序列,那么nums[i-1] 前面还有几个元素呢?这其实已经在之前计算 dp[i-1] 的时候计算好了。因此实际上这种情况的最大子序和是 dp[i-1] + nums[i] |
| 66 | + |
| 67 | +2. 自立门户以 nums[i] 开头形成子序列,这种浅情况就是 nums[i] |
| 68 | + |
| 69 | +基于贪心的思想,也可以统一成一个式子 max(dp[i-1], 0) + nums[i] |
| 70 | + |
| 71 | +接下来,我们考虑环。如果有环,那么最大子序和,要么就和普通的最大子序和一样只是普通的一段子序列,要么就是首尾两段加起来的和最大。 |
| 72 | + |
| 73 | +因此我们只需要额外考虑如何计算首尾两段的情况。对于这种情况其实等价于计算中间一段“最小子序和”,然后用数组的总和减去“最小子序和” |
| 74 | +就是答案。而求最小子序和和最大子序和基本没有差异,将 max 改为 min 即可。 |
| 75 | + |
| 76 | +## 关键点 |
| 77 | + |
| 78 | +- 其中一种情况(两段子序和):转化为 sum(nums) - 最小子序和 |
| 79 | + |
| 80 | +## 代码 |
| 81 | + |
| 82 | +- 语言支持:Python3 |
| 83 | + |
| 84 | +Python3 Code: |
| 85 | + |
| 86 | +```python |
| 87 | + |
| 88 | +class Solution: |
| 89 | + # 最小子序和 |
| 90 | + def solve1(self, A): |
| 91 | + A = A |
| 92 | + dp = [inf] * len(A) |
| 93 | + for i in range(len(A)): |
| 94 | + dp[i] = min(A[i], dp[i - 1] + A[i]) |
| 95 | + return min(dp) |
| 96 | + # 最大子序和 |
| 97 | + def solve2(self, A): |
| 98 | + A = A |
| 99 | + dp = [-inf] * len(A) |
| 100 | + for i in range(len(A)): |
| 101 | + dp[i] = max(A[i], dp[i - 1] + A[i]) |
| 102 | + return max(dp) |
| 103 | + def maxSubarraySumCircular(self, nums: List[int]) -> int: |
| 104 | + ans1 = sum(nums) - self.solve1(nums) |
| 105 | + ans2 = self.solve2(nums) |
| 106 | + if ans1 == 0: ans1 = max(nums) # 不能为空,那就选一个最大的吧 |
| 107 | + return max(ans1, ans2) |
| 108 | + |
| 109 | +``` |
| 110 | + |
| 111 | + |
| 112 | +**复杂度分析** |
| 113 | + |
| 114 | +令 n 为数组长度。 |
| 115 | + |
| 116 | +- 时间复杂度:$O(n)$ |
| 117 | +- 空间复杂度:$O(n)$ |
| 118 | + |
| 119 | + |
| 120 | + |
| 121 | + |
| 122 | +> 此题解由 [力扣刷题插件](https://leetcode-pp.github.io/leetcode-cheat/?tab=solution-template) 自动生成。 |
| 123 | +
|
| 124 | +力扣的小伙伴可以[关注我](https://leetcode-cn.com/u/fe-lucifer/),这样就会第一时间收到我的动态啦~ |
| 125 | + |
| 126 | +以上就是本文的全部内容了。大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 40K star 啦。大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。 |
| 127 | + |
| 128 | +关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。 |
| 129 | + |
| 130 | + |
0 commit comments