最大子序和算法经常遇到,虽然也能写出,但往往不能直接想到最优雅的写法,现总结与此,仅供学习交流使用。
Category | Difficulty | Likes | Dislikes |
---|---|---|---|
algorithms | Easy (43.94%) | 4563 | 168 |
Given an integer array nums
, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example:
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
Follow up:
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
这道题不难解,但是想写出最佳版代码确不见得那么容易,代码中有两个点值得注意:
temp_res
是初始化为 0 或者 -float(inf)
都行,但是 res
却是必须要初始化为 float(inf)
(不然无法完全覆盖数组值全负的情况);
要善用 max
, min
这类函数,比如本题中 temp_max
若是小于零需要重新赋值为 0 继续进行计算,但是实际上一行代码 max(temp_max, 0)
就完成了操作,不用 if
判断赋值操作;
以下提供一个比较优雅的代码版本
#
# @lc app=leetcode id=53 lang=python
#
# [53] Maximum Subarray
#
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
temp_max, nums_max = 0, -float('inf')
for count in nums:
temp_max = max(temp_max, 0) + count
nums_max = max(nums_max, temp_max)
return nums_max
Category | Difficulty | Likes | Dislikes |
---|---|---|---|
algorithms | Medium (32.06%) | 358 | 21 |
Here, a circular array means the end of the array connects to the beginning of the array. (Formally, C[i] = A[i]
when 0 <= i < A.length
, and C[i+A.length] = C[i]
when i >= 0
.)
Also, a subarray may only include each element of the fixed buffer A
at most once. (Formally, for a subarray C[i], C[i+1], ..., C[j]
, there does not exist i <= k1, k2 <= j
with k1 % A.length = k2 % A.length
.)
Example 1:
Input: [1,-2,3,-2]
Output: 3
Explanation: Subarray [3] has maximum sum 3
Example 2:
Input: [5,-3,5]
Output: 10
Explanation: Subarray [5,5] has maximum sum 5 + 5 = 10
Example 3:
Input: [3,-1,2,-1]
Output: 4
Explanation: Subarray [2,-1,3] has maximum sum 2 + (-1) + 3 = 4
Example 4:
Input: [3,-2,2,-3]
Output: 3
Explanation: Subarray [3] and [3,-2,2] both have maximum sum 3
Example 5:
Input: [-2,-3,-1]
Output: -1
Explanation: Subarray [-1] has maximum sum -1
Note:
-30000 <= A[i] <= 30000
1 <= A.length <= 30000
这道题就是将最大子序和算法套在了循环数组中,问题看似简单,但直接手撕还往往出错,自身改了三波还是没全对,需谨记这种循环数组解法的套路,将循环数组问题分情况处理:
对于结果不出现在首尾元素连用的情况,直接当做普通数组进行处理即可;
对于情况出现在首尾元素连用的情况,尝试将其通过变形依旧套在上一种首尾不相连情况中(如本题,通过将元素转变符号,巧妙地将首尾相连情况转换到不连的情况进行处理)
这个问题是之前 Leetcode 53:最大子序和的推广。
对于这个环形数组问题,我们会出现这样的两种情况不包含循环节点的子数组和包含循环节点(首尾节点)的子数组。对于不包含循环节点的子数组,我们直接使用 Kadane 算法即可。而对于包含循环节点的子数组,我们只要将 nums 中的每个元素取相反数,然后对这个相反数数组 -nums 运用 Kandane 算法就可以求解出 Kadane(-nums.subset)
(一定不包含循环节点,为什么?原来的最大变为了现在的最小,此时求解出来的实际上是原来最小值的相反数),然后我们将 sum(nums)+Kadane(-nums.subset)
就可以得到有循环节点的最大值。图示
但是这个思想有一个小 bug,当我们最后求得的最大值是由整个数组构成的话,那么这个问题就回到了第一个问题(不包含循环节点)。
举这样的例子:
-1, -2, -3
验算一下就会发现不合理的地方。这种情况也很好剔除,只要在程序的初始阶段检查一下max(nums)
是不是小于零即可。
class Solution:
def maxSubarraySumCircular(self, A):
"""
:type A: List[int]
:rtype: int
"""
def kadane(nums):
result = cur = float('-inf')
for num in nums:
cur = max(cur, 0) + num
result = max(result, cur)
return result
max_A = max(A)
if max_A < 0:
return max_A
result1 = kadane(A)
result2 = sum(A) + kadane([-num for num in A])
return max(result1, result2)
实际上我们可以将kadane
算法嵌入到现在的算法中,而不是单独处理,这样我们又得到了一个更加简洁的代码。
#
# @lc app=leetcode id=918 lang=python
#
# [918] Maximum Sum Circular Subarray
#
class Solution(object):
def maxSubarraySumCircular(self, A):
"""
:type A: List[int]
:rtype: int
"""
sum_a, cur_max, num_max, cur_min, num_min = 0, 0, -float('inf'), 0, float('inf')
for count in A:
cur_max = max(cur_max, 0) + count
num_max = max(num_max, cur_max)
cur_min = min(cur_min, 0) + count
num_min = min(num_min, cur_min)
sum_a += count
return max(num_max, sum_a - num_min) if num_max > 0 else num_max
[1] https://blog.csdn.net/qq_17550379/article/details/82965510