給定整數數組 (含正負數),找出**連續子數組**的最大和。若全負數,按題目要求可返回 0 (空子數組) 或最大負數。本文提供兩種經典解法完整 C 實現:**O(N) Kadane 線性掃描 (工程首選)** 與 **O(N log N) 分治遞歸 (教學經典)**,附隨機測試驅動代碼。
輸入: int a[N] (N=100, 含正負隨機數)
輸出: 最大連續子數組和
約束: 子數組必須連續;允許空子數組和為 0
核心洞察:維護「以當前元素結尾的最大子數組和」`thisSum`。若 `thisSum < 0`,拋棄前綴 (負數拖累後續),歸零重新開始。
int MaxSubSum_HighSpeed(int a[], int N) {
int thisSum = 0, maxSum = 0;
for (int i = 0; i < N; i++) {
thisSum += a[i]; // 延伸子數組
if (thisSum > maxSum) // 更新全局最大
maxSum = thisSum;
else if (thisSum < 0) // 負債重組
thisSum = 0;
}
return maxSum;
}
正確性證明草圖:最優子數組必不以負前綴開頭 (否則去掉前綴更大);算法枚舉所有「非負前綴結尾」的子數組和,覆蓋最優解。
複雜度:時間 O(N),空間 O(1)。—— **生產環境唯一推薦**。
策略:將數組二分,最大子數組必位於三種情況之一:
遞歸求解左右,線性掃描求跨越中點最大和,取三者最大。
// 三數取最大
int Max3(int a, int b, int c) {
if (a >= b && a >= c) return a;
if (b >= a && b >= c) return b;
return c;
}
// 遞歸核心
int MaxSubSequenceSum(int a[], int left, int right) {
if (left == right) { // 基例:單元素
return (a[left] > 0) ? a[left] : 0; // 允許空子數組返回 0
}
int center = (left + right) / 2;
int maxLeftSum = MaxSubSequenceSum(a, left, center);
int maxRightSum = MaxSubSequenceSum(a, center + 1, right);
// 跨越中點:向左掃描最大尾部和
int maxLeftBorderSum = 0, leftBorderSum = 0;
for (int i = center; i >= left; i--) {
leftBorderSum += a[i];
if (leftBorderSum > maxLeftBorderSum)
maxLeftBorderSum = leftBorderSum;
}
// 向右掃描最大頭部和
int maxRightBorderSum = 0, rightBorderSum = 0;
for (int i = center + 1; i <= right; i++) {
rightBorderSum += a[i];
if (rightBorderSum > maxRightBorderSum)
maxRightBorderSum = rightBorderSum;
}
return Max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum);
}
// 入口封裝
int MaxSubSum(int a[], int n) {
return MaxSubSequenceSum(a, 0, n - 1);
}
#include
#include
#include
#define MAX_NUM 100
// 簡易隨機數生成 (替代原文 random_n.h)
void random_n(int a[], int n) {
srand((unsigned)time(NULL));
for (int i = 0; i < n; i++)
a[i] = rand() % 200 - 100; // -99 ~ 100
}
int main() {
int a[MAX_NUM];
random_n(a, MAX_NUM);
// 算法 1: 線性掃描
int max1 = MaxSubSum_HighSpeed(a, MAX_NUM);
// 算法 2: 分治遞歸
int max2 = MaxSubSum(a, MAX_NUM);
printf("Array: ");
for (int i = 0; i < MAX_NUM; i++) printf("%d ", a[i]);
printf("\n");
printf("Kadane O(N) : %d\n", max1);
printf("Divide-Conquer O(NlogN): %d\n", max2);
printf("Results match: %s\n", (max1 == max2) ? "YES" : "NO");
return 0;
}
Array: 12 -45 78 23 -100 56 12 -3 90 -10 ...
Kadane O(N) : 245
Divide-Conquer O(NlogN): 245
Results match: YES
| 維度 | Kadane 線性掃描 | 分治遞歸 |
|---|---|---|
| 時間複雜度 | O(N) **最優** | O(N log N) |
| 空間複雜度 | O(1) **常數** | O(log N) 遞歸棧 |
| 代碼複雜度 | 極簡 (10 行) | 中等 (40 行) |
| 可並行化 | 困難 (強依賴前狀態) | 天然並行 (左右獨立) |
| 擴展性 | 易修改記錄子數組邊界 | 易擴展區間查詢 (段樹雛形) |
| 教學價值 | 貪心/動態規劃入門 | 分治法範例、主定理應用 |