1. 程式人生 > >Leetcode——53.最大子序和

Leetcode——53.最大子序和


@author: ZZQ
@software: PyCharm
@file: leetcode53_最大子序和.py
@time: 2018/11/26 12:39

要求:給定一個整數陣列 nums ,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。

示例:

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

方法如下:

方法一:暴力遍歷法——O(n2)

class Solution():
  def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        nums_len = len(nums)
        if nums_len == 0:
            return 0
        if nums_len == 1:
            return nums[0]
        max_value = nums[0]
        for i in range(nums_len):
            temp_max = 0
            for j in range(i, nums_len):
                temp_max += nums[j]  # 大量的重複運算,拖慢速度
                max_value = max(max_value, temp_max)
        return max_value

方法二:基於方法一避免大量的重複運算 ——O(n2)

class Solution():
  def maxSubArray(self, nums): 
        """
        :type nums: List[int]
        :rtype: int
        """
        nums_len = len(nums)
        temp_sum = [0]*(nums_len+1)
        if nums_len == 0:
            return 0
        if nums_len == 1:
            return nums[0]
        max_value = nums[0]
        for i in range(nums_len):   # 基於方法一,避免大量的重複運算
            temp_sum[i] = temp_sum[i-1] + nums[i]
        for i in range(nums_len):
            for j in range(i, nums_len):
                temp_max = temp_sum[j] - temp_sum[i-1]
                max_value = max(max_value, temp_max)
        return max_value

方法三:分治法——O(nlogn)

        將求長度為n的序列中的最大子序和華為分求兩個長度為n/2的序列的最大子序和
        將當前需要計算最大子串的序列分為兩半,
        前半段從後往前遍歷求最大子串lmax,
        後半段從前往後遍歷求最大子串rmax,
        lmax+rmax就是當前長度為n的序列的最大子串,
        然後遞迴去找長度為n/2的序列的最大子串和,不斷求小的序列的子串和。
        最後進行比較,得出最大值就是所求結果。
class Solution():
  def maxSubArray3(self, nums):  
        """
        :type nums: List[int]
        :rtype: int
        """
        nums_len = len(nums)
        if nums_len == 0:
            return 0
        left = 0
        right = nums_len - 1
        ans = self.maxSubArrayCompute(nums, left, right)
        return ans

    def maxSubArrayCompute(self, nums, left, right):
        """
        :type nums: List[int]
        :type left: int
        :type right: int
        :rtype: int
        """
        if left > right:
            return 0
        if left == right:
            return nums[left]
        middle = (left + right)/2
        left_max = self.maxSubArrayCompute(nums, left, middle)
        right_max = self.maxSubArrayCompute(nums, middle+1, right)
        lmax = nums[middle]
        left_sum = nums[middle]
        # 保證求得到子序列是連續的(也就是lmax + rmax是一個連續子序列的和)
        for i in range(middle-1, left-1, -1):  # 前半段逆序加(range(left,right,-1)表示逆序遍歷)
            left_sum += nums[i]
            lmax = max(lmax, left_sum)
        rmax = nums[middle+1]
        right_sum = nums[middle+1]
        for i in range(middle+2, right+1):  # 後半段順序加
            right_sum += nums[i]
            rmax = max(rmax, right_sum)
        return max(rmax+lmax, max(left_max, right_max))

方法四:掃描法——O(n)

        當我們加上一個正數時,和會增加;
        當我們加上一個負數時,和會減少。
        如果當前得到的和是個負數,那麼這個和在接下來的累加中應該拋棄並重新清零,不然的話這個負數將會減少接下來的和。
class Solution():
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        nums_len = len(nums)
        current = nums[0]
        nums_sum = nums[0]
        # 我們考慮如果全是負數,那麼返回最大的負數,如果最後的和為正,那麼就使用掃描法
        for i in range(1, nums_len):
            if current < 0:
                current = nums[i]  # 當前數小於0 肯定會捨去(否則將會影響接下來的和),換為下一個數
            else:
                current += nums[i]  # 如果當前數不小於0,那麼他會對接下來的和有積極影響
            if current > nums_sum:
                nums_sum = current  # 這裡既實現了負數返回最大也實現了掃描法
            #這裡其實已經隱式的列舉了所有可能,保留了所有可能的最大值
        return nums_sum

方法五:動態規劃——O(n)

        假設sum[i]為以i結尾的連續子串的和. 假設對於元素i,所有以它前面的元素結尾的子串和都已經求得,
        那麼以第i個元素結尾且和最大的連續子串實際上,
           1)要麼是以第i-1個元素結尾且和最大的連續子陣列加上這個元素,
           2)要麼只是第i個元素,即sum[i]
        所以當前的最大連續子串和的計算方法是max(sum[i-1]+nums[i], nums[i])
        這就是需要我們判斷sum[i-1]是否大於0。
        由於每次運算只需要前一次的結果,因此演算法的時間和空間複雜度都很小。
class Solution():
    def maxSubArray5(self, nums):  # 動態規劃
        """
        :type nums: List[int]
        :rtype: int
        """
        nums_len = len(nums)
        if nums_len == 0:
            return 0
        if nums_len == 1:
            return nums[0]
        nums_sum = nums[0]
        pre = nums[0]
        for i in range(1, nums_len):
            if pre > 0:
                pre += nums[i]
            else:
                pre = nums[i]
            nums_sum = max(nums_sum, pre)

        return nums_sum