1. 程式人生 > >清晰解題: 尋找最大子數列-Kadane演算法

清晰解題: 尋找最大子數列-Kadane演算法

  • 問題: 給定一個數列,例如【−2, 1, −3, 4, −1, 2, 1, −5, 4】, 求一個連續的數列使得數列內的元素和最大, 示例中最大子數列應該是【4, −1, 2, 1】, 求和值為6。

  • 這個問題是可以衍生到一些變種問題, 如尋找數列中最大乘積序列,且要求序列中,相鄰元素間隔不超過限定值等, 常出現在筆試面試程式設計題中。

  • 該問題最早於1977年提出,但是直到1984年才被Jay Kadane 發現了線性時間的最優解法,所以演算法雖然長度很短,但其實並不容易理解。

  • 演算法描述:

    • 遍歷該陣列, 在遍歷過程中, 將遍歷到的元素依次累加起來, 當累加結果小於或等於0時, 從下一個元素開始,重新開始累加。
    • 累加過程中, 要用一個變數(max_so_far)記錄所獲得過的最大值
    • 一次遍歷之後, 變數 max_so_far 中儲存的即為最大子片段的和值。

此處為python 程式碼 , 變數A 傳入陣列。

def max_subarray(A):
    max_ending_here = max_so_far = 0
    for x in A:
        max_ending_here = max(0, max_ending_here + x)
        max_so_far = max(max_so_far, max_ending_here)
    return max_so_far
  • 首先, 題目中有一個隱含的設定, 最大子片段是可以為空的, 空片段的和值是0。 這一點必須要明確, 不然會無法理解演算法的正確性, 所以當給定的數列中,求和為負數的時候,例如【-2,1, -3】, 演算法會返回最大求和值0, 因為預設該陣列的最大子片段是一個空序列。

  • 理解此演算法的關鍵在於:

    • 最大子片段中不可能包含求和值為負的字首。 例如 【-2, 1,4】 必然不能是最大子數列, 因為去掉值為負的字首後【-2,1】, 可以得到一個更大的子數列 【4】、
    • 所以在遍歷過程中,每當累加結果成為一個非正值時, 就應當將下一個元素作為潛在最大子數列的起始元素, 重新開始累加。
    • 由於在累加過程中, 出現過的最大值都會被記錄, 且每一個可能成為 最大子數列起始元素
      的位置, 都會導致新一輪的累加, 這樣就保證了答案搜尋過程的完備性和正確性。