题目解析
问题描述 给定一个长度为 nn 的环形整数数组 nums,需要找到该数组中的非空子数组的最大可能和。环形数组的特点是它的末端和开头相连,形式上,nums[i] 的下一个元素是 nums[(i + 1) % n],而 nums[i] 的前一个元素是 nums[(i - 1 + n) % n]。
示例
- 输入:
nums = [-1, -2, 3, -2],输出:3 - 输入:
nums = [-5, -3, 5],输出:5 - 输入:
nums = [-3, -1, 2, -1],输出:2 - 输入:
nums = [-2, -3, -1],输出:-1
思路
- 线性子数组的最大和:使用 Kadane's Algorithm 找到线性数组的最大子数组和。
- 跨越数组末端的最大和:计算整个数组的总和,然后减去最小的子数组和(使用 Kadane's Algorithm 的变种找到最小子数组和)。这样可以得到跨越数组末端的最大子数组和。
- 特殊情况处理:如果所有元素都是负数,那么跨越数组末端的最大和会变成 0,这不符合题意,因此在这种情况下,我们直接返回线性子数组的最大和。
图解 假设 nums = [-1, -2, 3, -2]:
-
线性子数组的最大和:
- 使用 Kadane's Algorithm,从左到右遍历数组,找到最大子数组和。
[-1, -2, 3, -2]的最大子数组和为3。
-
跨越数组末端的最大和:
- 计算整个数组的总和:
-1 + (-2) + 3 + (-2) = -2。 - 使用 Kadane's Algorithm 的变种找到最小子数组和:
-2。 - 跨越数组末端的最大和为
总和 - 最小子数组和 = -2 - (-2) = 0。 - 由于所有元素都是负数,返回线性子数组的最大和
3。
- 计算整个数组的总和:
代码详解
python
def kadane_max_subarray_sum(nums):
max_current = max_global = nums[0]
for num in nums[1:]:
max_current = max(num, max_current + num)
max_global = max(max_global, max_current)
return max_global
def kadane_min_subarray_sum(nums):
min_current = min_global = nums[0]
for num in nums[1:]:
min_current = min(num, min_current + num)
min_global = min(min_global, min_current)
return min_global
def solution(nums: list) -> int:
max_linear_sum = kadane_max_subarray_sum(nums)
total_sum = sum(nums)
min_subarray_sum = kadane_min_subarray_sum(nums)
# Handle the case where all elements are negative
if min_subarray_sum == total_sum:
return max_linear_sum
max_circular_sum = total_sum - min_subarray_sum
return max(max_linear_sum, max_circular_sum)
if __name__ == '__main__':
print(solution([-1, -2, 3, -2]) == 3) # 应该为 True
print(solution([-5, -3, 5]) == 5) # 应该为 True
print(solution([-3, -1, 2, -1]) == 2) # 应该为 True
print(solution([-2, -3, -1]) == -1) # 应该为 True
知识点具体解释
1. Kadane's Algorithm
定义与原理 Kadane's Algorithm 是一种用于找到线性数组中最大子数组和的经典算法。它的时间复杂度为 O(n),非常高效。算法的核心思想是通过动态规划的思想,维护一个当前子数组的最大和,不断更新全局最大和。
步骤
-
初始化:
max_current:当前子数组的最大和,初始值为数组的第一个元素。max_global:全局最大和,初始值为数组的第一个元素。
-
遍历数组:
- 对于数组中的每一个元素
num,更新max_current为max(num, max_current + num),即当前元素和当前子数组的最大和之间的较大值。 - 更新
max_global为max(max_global, max_current),即全局最大和和当前子数组的最大和之间的较大值。
- 对于数组中的每一个元素
-
返回结果:
- 返回
max_global,即全局最大和。
- 返回
代码实现
python
def kadane_max_subarray_sum(nums):
max_current = max_global = nums[0]
for num in nums[1:]:
max_current = max(num, max_current + num)
max_global = max(max_global, max_current)
return max_global
示例 假设 nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]:
-
初始化:
max_current = -2,max_global = -2 -
遍历数组:
num = 1:max_current = max(1, -2 + 1) = 1,max_global = max(-2, 1) = 1num = -3:max_current = max(-3, 1 - 3) = -2,max_global = max(1, -2) = 1num = 4:max_current = max(4, -2 + 4) = 4,max_global = max(1, 4) = 4num = -1:max_current = max(-1, 4 - 1) = 3,max_global = max(4, 3) = 4num = 2:max_current = max(2, 3 + 2) = 5,max_global = max(4, 5) = 5num = 1:max_current = max(1, 5 + 1) = 6,max_global = max(5, 6) = 6num = -5:max_current = max(-5, 6 - 5) = 1,max_global = max(6, 1) = 6num = 4:max_current = max(4, 1 + 4) = 5,max_global = max(6, 5) = 6
-
返回结果:
max_global = 6
2. Kadane's Algorithm 的变种
定义与原理 Kadane's Algorithm 的变种用于找到线性数组中的最小子数组和。其基本思路与原版相似,只是将最大值的比较改为最小值的比较。
步骤
-
初始化:
min_current:当前子数组的最小和,初始值为数组的第一个元素。min_global:全局最小和,初始值为数组的第一个元素。
-
遍历数组:
- 对于数组中的每一个元素
num,更新min_current为min(num, min_current + num),即当前元素和当前子数组的最小和之间的较小值。 - 更新
min_global为min(min_global, min_current),即全局最小和和当前子数组的最小和之间的较小值。
- 对于数组中的每一个元素
-
返回结果:
- 返回
min_global,即全局最小和。
- 返回
代码实现
python
def kadane_min_subarray_sum(nums):
min_current = min_global = nums[0]
for num in nums[1:]:
min_current = min(num, min_current + num)
min_global = min(min_global, min_current)
return min_global
示例 假设 nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]:
-
初始化:
min_current = -2,min_global = -2 -
遍历数组:
num = 1:min_current = min(1, -2 + 1) = -1,min_global = min(-2, -1) = -2num = -3:min_current = min(-3, -1 - 3) = -4,min_global = min(-2, -4) = -4num = 4:min_current = min(4, -4 + 4) = 0,min_global = min(-4, 0) = -4num = -1:min_current = min(-1, 0 - 1) = -1,min_global = min(-4, -1) = -4num = 2:min_current = min(2, -1 + 2) = 1,min_global = min(-4, 1) = -4num = 1:min_current = min(1, 1 + 1) = 1,min_global = min(-4, 1) = -4num = -5:min_current = min(-5, 1 - 5) = -4,min_global = min(-4, -4) = -4num = 4:min_current = min(4, -4 + 4) = 0,min_global = min(-4, 0) = -4
-
返回结果:
min_global = -4
3. 特殊情况处理
定义与原理 在处理环形数组的最大子数组和问题时,需要特别处理所有元素都是负数的情况。如果所有元素都是负数,那么跨越数组末端的最大和会变成 0,这不符合题意,因此在这种情况下,我们直接返回线性子数组的最大和。
步骤
-
计算线性子数组的最大和:使用 Kadane's Algorithm 找到线性数组的最大子数组和。
-
计算整个数组的总和:计算数组中所有元素的总和。
-
计算最小子数组和:使用 Kadane's Algorithm 的变种找到线性数组的最小子数组和。
-
处理特殊情况:
- 如果最小子数组和等于整个数组的总和,说明所有元素都是负数,直接返回线性子数组的最大和。
-
计算跨越数组末端的最大和:用整个数组的总和减去最小子数组和,得到跨越数组末端的最大子数组和。
-
返回结果:返回线性子数组的最大和和跨越数组末端的最大和中的较大值。
代码实现
python
def solution(nums: list) -> int:
max_linear_sum = kadane_max_subarray_sum(nums)
total_sum = sum(nums)
min_subarray_sum = kadane_min_subarray_sum(nums)
# Handle the case where all elements are negative
if min_subarray_sum == total_sum:
return max_linear_sum
max_circular_sum = total_sum - min_subarray_sum
return max(max_linear_sum, max_circular_sum)
示例 假设 nums = [-1, -2, 3, -2]:
- 计算线性子数组的最大和:
max_linear_sum = 3 - 计算整个数组的总和:
total_sum = -1 + (-2) + 3 + (-2) = -2 - 计算最小子数组和:
min_subarray_sum = -2 - 处理特殊情况:
min_subarray_sum != total_sum - 计算跨越数组末端的最大和:
max_circular_sum = total_sum - min_subarray_sum = -2 - (-2) = 0 - 返回结果:
max(max_linear_sum, max_circular_sum) = max(3, 0) = 3
可能出现的问题及解决办法
- 所有元素都是负数:如果所有元素都是负数,
min_subarray_sum会等于total_sum,导致max_circular_sum为 0。这种情况下,直接返回max_linear_sum。 - 数组为空:在实际应用中,需要处理数组为空的情况,返回适当的错误信息或默认值。
- 边界条件:在实现 Kadane's Algorithm 时,需要注意边界条件,确保在数组只有一个元素时也能正确处理。
总结 通过这道题目,我学会了如何使用 Kadane's Algorithm 及其变种来解决最大子数组和问题,并且掌握了处理环形数组的方法。希望这些经验和方法能帮助其他同学更好地理解和解决类似问题。