1. 程式人生 > 實用技巧 >資料結構與演算法之【最大子列和問題】

資料結構與演算法之【最大子列和問題】

題目

【劍指offer】42.連續子陣列的最大和

輸入一個整型陣列,陣列中的一個或連續多個整陣列成一個子陣列。求所有子陣列的和的最大值。

示例1

輸入: nums = [-2,1,-3,4,-1,2,1,-5,4]
輸出: 6
解釋: 連續子陣列 [4,-1,2,1] 的和最大,為 6。

思路1:暴力破解法,將所有的子列和都列舉出來。

class Solution:
    def maxSubArray(self, nums):  # 暴力求解
        '''
        :param nums: list
        :return: 最大子序列和
        '''
        assert isinstance(nums, list), "nums不是一個list"

        l = len(listA)
        MaxSum = 0
        for i in range(l):  # 子序列左段
            TheSum = 0
            for j in range(i, l):  # 子序列右端
                TheSum += nums[j]
                if TheSum > MaxSum:
                    MaxSum = TheSum
        return MaxSum

思路2:分而治之。“分”就是將大問題轉化為小問題,“治”就是統一起來。演算法思路:

  • 情況1:子列和最大值出現在子序列的左段;
  • 情況2: 子列和最大值出現在子序列的右端;
  • 情況3:子列和最大值出現在橫跨兩端的子列中;
def maxSubArray2(self, nums, left=0, right=0):  # 分而治之
    '''
    :param nums: list
    :param left: 求最大子列的起始下標
    :param right: 終止下標
    :return: 最大子序列和
    '''

    assert isinstance(nums, list), "nums不是一個list"
    assert left <= right, "輸入錯誤,left>right"

    s = 0
    midSum = 0
    leftSum = 0
    rightSum = 0
    if left == right:
        s = nums[left]
    else:
        center = (left + right) // 2
        # print("center",center)
        leftSum = self.maxSubArray2(nums, left, center)  # 情況1 最大欄位和全部取左邊元素
        rightSum = self.maxSubArray2(nums, center + 1, right)  # 情況2 最大欄位和全部取右邊元素
        s1 = 0;
        lefts = 0  # 求出s1, 從中間到左邊的最大和
        for i in range(center, left - 1, -1):
            lefts += nums[i]
            if s1 < lefts:
                s1 = lefts
        # print("s1:", s1)
        s2 = 0
        rights = 0  # 求出s2, 從中間到右邊的最大和
        for i in range(center + 1, right, 1):
            rights += nums[i]
            if s2 < rights:
                s2 = rights
        # print("s2:", s2)
        midSum = s1 + s2  # 橫跨中間的最大子段和
        s = max([midSum, leftSum, rightSum])

思路3:線上處理。優勢在於對於任意的輸入的子序列,都可以直接輸出當前的最大子列和;重點在於當前的子列和能不能對後續的輸入有作用,如果為最大子列和為負數則歸零,如果加入新的值後當前子列和更大則更新最大子列和的值。

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        '''
        :param nums: list
        :return: 最大子序列和
        '''
        l = len(nums)
        maxSum = nums[0]
        ThisSum = 0
        for i in range(l):
            ThisSum += nums[i]
            if ThisSum > maxSum: # 如果當前子列和更大,則更新masSum
                maxSum = ThisSum
            if ThisSum < 0: # 如果當前子列和<0,則必然不包括再最大子列和中
                ThisSum = 0
        return maxSum

提高:除了輸出最大子列和以外,還要輸出該最大子列的起始值和終了值。如果最大子序列不是唯一的,則輸出索引i和j最小的子序列(如示例所示)。 如果所有K個數字均為負,則其最大和定義為0,並且應該輸出整個序列的第一個和最後一個數字。

def maxSubarray3(self, nums):  # 線上處理
    '''
    :param nums: list
    :return: 最大子序列和
    '''
    l = len(nums)
    maxSum = -1
    ThisSum = 0
    left_index, right_index, temp_index = 0, l - 1, 0  # 初始化很重要,對於需要返回下標的時候
    for i in range(l):
        ThisSum += nums[i]
        if ThisSum < 0:
            ThisSum = 0
            temp_index = i + 1
        elif ThisSum > maxSum:
            maxSum = ThisSum
            left_index = temp_index
            right_index = i
    if maxSum < 0:  maxSum = 0
    return maxSum, left_index, right_index