數據結構-學習之路(一)-子序列問題優化

**本博客內容根據 Mooc 浙江大學 數據結構 1.3課 什麼是算法一課總結獲得
若是對於博客中的算法有任何疑問請您斧正**python


問題: 子序列問題

有一個序列N, 其中包含了 [A1,.... AN]個整數, 求解獲得其中的最大連續子序列和。

例子: 
N: [1, 2, -1]
幾個連續子序列:
[1]
[2]
[-1]
[1, 2]
[2, -1]
[1, 2, -1]
其中最大的連續子序列就是3

面對這個問題, 最暴力的方式就是,窮舉獲得全部的子序列,而後獲得其中最大的子序列和。算法

C1 循環法:

def get_max_sequence_sum(nums: list) -> int:
    length = len(nums)
    this_sum = max_sum = 0
    # 遍歷子序列的起始index
    for first_index in range(length):
        # 遍歷子序列的終止index
        for end_index in range(first_index, length):
            # 計算獲得當前的子序列和
            for num in nums[first_index: end_index+1]:
                this_sum += num
            max_sum = this_sum if this_sum > max_sum else max_sum
            this_sum = 0
    return max_sum

對於這個算法, 在計算的過程當中進行了3次循環,T(n) = n ^ 3數據結構


明顯能夠發現,在計算子序列和的時候,能夠經過上一個子序列和加上下一個整數獲得新子序列和優化

C2 循環法2.0

def get_max_sequence_sum(nums: list) -> int:
    length = len(nums)
    this_sum = max_sum = 0
    # 遍歷子序列的起始index
    for first_index in range(length):
        # 遍歷子序列的終止index
        for end_index in range(first_index, length):
            # 當前子序列和 爲 上一個子序列和加上當前整數
            this_sum += nums[end_index]
            max_sum = this_sum if this_sum > max_sum else max_sum
        this_sum = 0
    return max_sum

經過對於子序列和的優化後, 能夠減小每次計算子序列和的循環, T(n) = n ^ 2this


在進行減小循環的優化後,咱們還能夠進一步的使用,分治法的思想來優化這個算法。spa

對於最大子序列和問題,咱們能夠將問題分解成code

將序列按照中間分紅左右兩個序列
而後計算 左子序列的最大子序列和 右子序列的最大子序列和 以及從中間往兩邊掃描獲得的最大子序列和

子序列問題分治法.png
image.png

第一步 獲得左右兩邊序列 [4, -3, 5, 2], [-1, 2, 6, 2]
第二步 對於左右序列計算最大子序列和
      左序列繼續分解成 [4, -1], [5, 2]
      左左繼續分解成 [4],[-1]; [5], [2]
      獲得左左左 序列和最大爲 4
      左左右 序列最大和爲5
      而後左序列[4, -3,|5, 2]從分割線往兩邊獲得最大子序列和爲6
      所以左序列的最大子序列和爲6
      以此類推 右序列的最大子序列和爲 8
第三步: 最初序列從分割線往左右兩邊掃描的最大序列和爲11
第四步: 比較三個部分的和 獲得最大值爲11
(寫起來, 比較複雜 QAQ ,若是看不懂你們能夠看下參考課程老師的視頻)

分治法的思路 就是不斷分解問題 而後再將子問題的結果合起來獲得最終結果

從而, 咱們能夠獲得第三種算法視頻

C3 分治法

def get_max_sequence_sum(nums: list) -> int:

    length = len(nums)
    middle_index = length // 2
    middle_to_left_this_sequence_sum = middle_to_left_max_sequence_sum = 0
    middle_to_right_this_sequence_sum = middle_to_right_max_sequence_sum = 0

    # 遞歸終止條件
    if length == 0:
        return 0
    if length == 1:
        return nums[0] if nums[0] > 0 else 0
    # 經過遞歸的方式獲得 左邊子序列 和 右邊子序列的最大子序列和
    left_max_sequence_sum = get_max_sequence_sum(nums[:middle_index])
    right_max_sequence_sum = get_max_sequence_sum(nums[middle_index:])

    # 計算從中間往兩邊掃描獲得的最大子序列和

    # 從中間往左邊掃描獲得最大的
    for num in nums[:middle_index]:
        middle_to_left_this_sequence_sum += num
        middle_to_left_max_sequence_sum = middle_to_left_this_sequence_sum if \
            middle_to_left_this_sequence_sum > middle_to_left_max_sequence_sum else \
            middle_to_left_max_sequence_sum

    # 從中間往右邊掃描獲得最大的
    for num in nums[middle_index:]:
        middle_to_right_this_sequence_sum += num
        middle_to_right_max_sequence_sum = middle_to_right_this_sequence_sum if \
            middle_to_right_this_sequence_sum > middle_to_right_max_sequence_sum else \
            middle_to_right_max_sequence_sum

    middle_max_sequence_sum = middle_to_left_max_sequence_sum + middle_to_right_max_sequence_sum
    return max([left_max_sequence_sum, right_max_sequence_sum, middle_max_sequence_sum])

利用這種分治法, 每次都會將問題分解一半, 而後掃描對應整個序列, 所以最後 T(n) = nlogn。blog


通常狀況下利用分治法, 咱們已經能夠獲得比較優化的解法, 可是對於某些問題 咱們還能夠經過更爲精妙的數學思路來優化問題。遞歸

例如對於子序列和的計算問題, 由於是連續子序列, 若是獲得的某個子序列和 < 0,那咱們就徹底能夠拋棄掉這個子序列,而直接從下一個數繼續往下獲得新的子序列和。這種處理方法也叫作"在線處理問題"。

C4 在線處理法

def get_max_sequence_sum_4(nums: list) -> int:
    this_sum = max_sum = 0
    for num in nums:
        this_sum += num
        max_sum = this_sum if this_sum > max_sum else max_sum
        # 若是當前子序列和 < 0 直接拋棄這個子序列
        if this_sum < 0:
            this_sum = 0
    return max_sum

咱們能夠很明顯的看到,這種算法的, 平均時間複雜度爲n。


參考

Mooc-數據結構

相關文章
相關標籤/搜索