算法与数据结构(十五):最大子序和算法总结

最大子序和算法总结

最大子序和算法经常遇到,虽然也能写出,但往往不能直接想到最优雅的写法,现总结与此,仅供学习交流使用。

LeetCode 53. Maximum Subarray

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.

这道题不难解,但是想写出最佳版代码确不见得那么容易,代码中有两个点值得注意:

  1. temp_res 是初始化为 0 或者 -float(inf) 都行,但是 res 却是必须要初始化为 float(inf)(不然无法完全覆盖数组值全负的情况);

  2. 要善用 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


LeetCode 918. Maximum Sum Circular Subarray

Category Difficulty Likes Dislikes
algorithms Medium (32.06%) 358 21
Tags Companies Given a **circular array** **C** of integers represented by `A`, find the maximum possible sum of a non-empty subarray of **C**.

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:

  1. -30000 <= A[i] <= 30000
  2. 1 <= A.length <= 30000

这道题就是将最大子序和算法套在了循环数组中,问题看似简单,但直接手撕还往往出错,自身改了三波还是没全对,需谨记这种循环数组解法的套路,将循环数组问题分情况处理:

  1. 结果出现在包含首尾元素连用的情况;
  2. 结果不出现在首尾元素连用的情况。

对于结果不出现在首尾元素连用的情况,直接当做普通数组进行处理即可;

对于情况出现在首尾元素连用的情况,尝试将其通过变形依旧套在上一种首尾不相连情况中(如本题,通过将元素转变符号,巧妙地将首尾相连情况转换到不连的情况进行处理)

解题思路

这个问题是之前 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

你可能感兴趣的:(算法与数据结构)