想像你有一排數字,這些數字可以是正數、負數或零。例如:[-2, 1, -3, 4, -1, 2, 1, -5] 我們想在這排數字中,找到連續的一段,使得這段數字的總和最大。
[4, -1, 2, 1] 這一段的和是 4 - 1 + 2 + 1 = 6。[1, -3] 這一段的和是 1 - 3 = -2。[4] 的和是 4。我們的目標就是找出那個最大的和。
如果用「笨」方法?
最直觀的方法可能是:
Kadane's Algorithm 的聰明之處 (核心思想)
Kadane 演算法說:我們不需要看所有的子序列!我們可以從頭到尾掃描一次數列,同時記住一些關鍵資訊。
想像你正走過這排數字,手裡拿著兩個計分板:
目前走到這裡的最佳連續和 (current_max_ending_here):
到目前為止的全局最佳連續和 (overall_max_so_far):
演算法步驟 (一步一步來):
初始化:
overall_max_so_far = 數列的第一個數字。current_max_ending_here = 數列的第一個數字。 (如果數列是空的,或者題目有特殊規定,可能初始化方式不同,但我們先假設數列至少有一個數字)。遍歷數列: 從數列的第二個數字開始,一個一個往後看。對於你看到的每一個新數字 num:
current_max_ending_here:
num,讓它作為結尾的子序列,怎樣才能得到最大和?有兩種可能:
num 自己單獨一段,形成一個新的開始。它的和就是 num 本身。num 接到「以上一個數字結尾的最佳連續和 (current_max_ending_here 的舊值)」後面。它的和就是 舊的 current_max_ending_here + num。current_max_ending_here。 current_max_ending_here = max(num, current_max_ending_here + num)overall_max_so_far:
overall_max_so_far = max(overall_max_so_far, current_max_ending_here)結束: 當你看完所有數字後,overall_max_so_far 計分板上的數字,就是整個數列中最大連續子序列的和。
為什麼這樣可行?(處理負數的關鍵)
current_max_ending_here = max(num, current_max_ending_here + num) 這一行是精髓。
current_max_ending_here + num 比 num 自己還小,這意味著前面那段 current_max_ending_here 是個「拖油瓶」(它的和是負的,而且負得比較多)。與其帶著這個拖油瓶讓總和變小,不如從 num 自己開始一段新的子序列。舉個例子:[-2, 1, -3, 4, -1, 2, 1, -5]
| 當前數字 (num) | current_max_ending_here 計算 (max(num, prev_current_max + num)) | current_max_ending_here 值 | overall_max_so_far 計算 (max(prev_overall_max, current_max)) | overall_max_so_far 值 | 備註 |
|---|---|---|---|---|---|
| -2 (初始) | -2 | -2 | 初始化兩個變數 | ||
| 1 | max(1, -2 + 1) = max(1, -1) | 1 | max(-2, 1) | 1 | 從 1 重新開始一段更優 |
| -3 | max(-3, 1 + (-3)) = max(-3, -2) | -2 | max(1, -2) | 1 | 1+(-3) = -2,比單獨-3好 |
| 4 | max(4, -2 + 4) = max(4, 2) | 4 | max(1, 4) | 4 | 從 4 重新開始一段更優 |
| -1 | max(-1, 4 + (-1)) = max(-1, 3) | 3 | max(4, 3) | 4 | 4+(-1) = 3,比單獨-1好 |
| 2 | max(2, 3 + 2) = max(2, 5) | 5 | max(4, 5) | 5 | 3+2 = 5,比單獨2好 |
| 1 | max(1, 5 + 1) = max(1, 6) | 6 | max(5, 6) | 6 | 5+1 = 6,比單獨1好 |
| -5 | max(-5, 6 + (-5)) = max(-5, 1) | 1 | max(6, 1) | 6 | 6+(-5) = 1,比單獨-5好 |
遍歷結束,overall_max_so_far 的值是 6。
處理全為負數的情況:
例如 [-1, -2, -3]
| 當前數字 (num) | current_max_ending_here 計算 | current_max_ending_here 值 | overall_max_so_far 計算 | overall_max_so_far 值 |
|---|---|---|---|---|
| -1 (初始) | -1 | -1 | ||
| -2 | max(-2, -1 + (-2)) = max(-2, -3) | -2 | max(-1, -2) | -1 |
| -3 | max(-3, -2 + (-3)) = max(-3, -5) | -3 | max(-1, -3) | -1 |
答案是 -1,這是正確的(因為 [-1] 是和最大的子序列)。
總結一下:
Kadane's Algorithm 的核心就是:
current_max_ending_here)
overall_max_so_far)。這樣只需要從頭到尾看一遍數列 (O(N) 時間),用很少的額外記憶體 (O(1) 空間),就能找到答案!對於中級學生來說,理解這個遞推的想法和「如果累計和變成負的就可能需要重新開始」的直覺是很重要的一步。