**本博客內容根據 Mooc 浙江大學 數據結構 1.3課 什麼是算法一課總結獲得
若是對於博客中的算法有任何疑問請您斧正**python
有一個序列N, 其中包含了 [A1,.... AN]個整數, 求解獲得其中的最大連續子序列和。 例子: N: [1, 2, -1] 幾個連續子序列: [1] [2] [-1] [1, 2] [2, -1] [1, 2, -1] 其中最大的連續子序列就是3
面對這個問題, 最暴力的方式就是,窮舉獲得全部的子序列,而後獲得其中最大的子序列和。算法
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數據結構
明顯能夠發現,在計算子序列和的時候,能夠經過上一個子序列和加上下一個整數獲得新子序列和優化
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
將序列按照中間分紅左右兩個序列 而後計算 左子序列的最大子序列和 右子序列的最大子序列和 以及從中間往兩邊掃描獲得的最大子序列和
第一步 獲得左右兩邊序列 [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 ,若是看不懂你們能夠看下參考課程老師的視頻) 分治法的思路 就是不斷分解問題 而後再將子問題的結果合起來獲得最終結果
從而, 咱們能夠獲得第三種算法視頻
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,那咱們就徹底能夠拋棄掉這個子序列,而直接從下一個數繼續往下獲得新的子序列和。這種處理方法也叫作"在線處理問題"。
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。