Leetcode——53.最大子序和
阿新 • • 發佈:2018-11-26
@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