算法與數據結構(十五):最大子序和算法總結

最大子序和算法總結

最大子序和算法經常遇到,雖然也能寫出,但往往不能直接想到最優雅的寫法,現總結與此,僅供學習交流使用。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章