leetcode熱題100--最大子序和

53. 最大子序和

題目描述

給定一個整數數組 nums ,找到一個具有最大和的連續子數組(子數組最少包含一個元素),返回其最大和。
示例:
在這裏插入圖片描述

題解

**1.方法動態規劃

> 第 1 步:定義狀態
既然一個連續子數組一定要以一個數作爲結尾,那麼我們就將狀態定義成如下。
dp[i]:表示以 nums[i] 結尾的連續子數組的最大和。

第 2 步:思考狀態轉移方程
根據狀態的定義,由於 nums[i] 一定會被選取,並且 dp[i] 所表示的連續子序列與 dp[i - 1] 所表示的連續子序列(有可能)就差一個 nums[i] 。

假設數組 nums 全是正數,那麼一定有 dp[i] = dp[i - 1] + nums[i]。

在一般情況下 dp[i - 1] 有可能是負數,例如前幾個數都是負數,突然來了一個正數。

於是分類討論:

如果 dp[i - 1] >= 0,那麼可以把 nums[i] 直接接在 dp[i - 1] 表示的那個數組的後面。

如果 dp[i - 1] < 0,那麼加上前面的數反而越來越小了,於是“另起爐竈”,單獨的一個 nums[i],就是 dp[i]。
在這裏插入圖片描述

第一個子組合是以第一個數字結尾的連續序列,也就是[-2],最大值-2

第二個子組合是以第二個數字結尾的連續序列,也就是[-2,1], [1],最大值1

第三個子組合是以第三個數字結尾的連續序列,也就是[-2,1]+[,3], [1]+[3], [3],最大值4
繼承子組合二得到的序列,也就是[-2,1,3], [1,3] (最大值 = 第二個組合的最大值 + 第三個數字)
單獨第三個數字的序列,也就是[3] (最大值 = 第三個數字)**

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        maxSum = nums[0]
        maxContinuousSum = 0 # 到當前值的最大連續和
        for num in nums:
            if maxContinuousSum > 0:
                maxContinuousSum = maxContinuousSum + num
            else:
                maxContinuousSum = num
            maxSum = max(maxSum, maxContinuousSum)
        return maxSum
from typing import List


class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        size = len(nums)
        if size == 0:
            return 0
        dp = [0 for _ in range(size)] #全部都是0

        dp[0] = nums[0]
        for i in range(1, size):
            dp[i] = max(dp[i - 1] + nums[i], nums[i])
        return max(dp)

複雜度分析

時間複雜度:O(N)。只遍歷一次數組。
空間複雜度:O(1),只使用了常數空間。

  1. 分治法

治法是將整個數組切分成幾個小組,每個小組然後再切分成幾個更小的小組,一直到不能繼續切分也就是隻剩一個數字爲止。然後每個小組會計算出最優值,彙報給上一級的小組,一級一級彙報,上級拿到下級的彙報找到最大值,得到最終的結果。和合並排序的算法類似,先切分,再合併結果。

這個問題中的關鍵就是如何切分這些組合才能使每個小組之間不會有重複的組合(有重複的組合意味着有重複的計算量),這個問題應該困擾了不少的同學,我在學習理解的時候也花了不少時間。

首先是切分分組方法,就這個案例中的例子來,我們有一個數組[-2,1,-3,4,-1,2,1,-5,4],一共有9個元素,我們center=(start+ end) / 2這個原則,得到中間元素的索引爲4,也就是-1,拆分成三個組合:
[-2,1,-3,4,-1]以及它的子序列(在-1左邊的並且包含它的爲一組)
[2,1,-5,4]以及它的子序列(在-1右邊不包含它的爲一組)
任何包含-1以及它右邊元素2的序列爲一組(換言之就是包含左邊序列的最右邊元素以及右邊序列最左邊元素的序列,比如[4,-1,2,1],這樣就保證這個組合裏面的任何序列都不會和上面兩個重複)
在這裏插入圖片描述
以上的三個組合內的序列沒有任何的重複的部分,而且一起構成所有子序列的全集,計算出這個三個子集合的最大值,然後取其中的最大值,就是這個問題的答案了。

from typing import List


class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        size = len(nums)
        if size == 0:
            return 0
        return self.__max_sub_array(nums, 0, size - 1)

    def __max_sub_array(self, nums, left, right):
        if left == right:
            return nums[left]
        mid = (left + right) >> 1
        return max(self.__max_sub_array(nums, left, mid),
                   self.__max_sub_array(nums, mid + 1, right),
                   self.__max_cross_array(nums, left, mid, right))

    def __max_cross_array(self, nums, left, mid, right):
        # 一定包含 nums[mid] 元素的最大連續子數組的和,
        # 思路是看看左邊"擴散到底",得到一個最大數,右邊"擴散到底"得到一個最大數
        # 然後再加上中間數
        left_sum_max = 0
        start_left = mid - 1
        s1 = 0
        while start_left >= left:
            s1 += nums[start_left]
            left_sum_max = max(left_sum_max, s1)
            start_left -= 1

        right_sum_max = 0
        start_right = mid + 1
        s2 = 0
        while start_right <= right:
            s2 += nums[start_right]
            right_sum_max = max(right_sum_max, s2)
            start_right += 1
        return left_sum_max + nums[mid] + right_sum_max


複雜度分析:

時間複雜度:O(NlogN),這裏遞歸的深度是對數級別的,每一層需要遍歷一遍數組(或者數組的一半、四分之一)。
空間複雜度:O(1),僅需要常數個空間用於選取最大值。

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