🔍 问题描述
最大子段和问题(Maximum Subarray Problem)是算法领域的经典问题:
-
给定一个整数数组(可能包含负数)
-
找出一个连续子数组(至少包含一个元素)
-
使得该子数组的和最大
-
返回这个最大和值
- 示例:
输入:[-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大为6
🧠 算法思路:动态规划
核心思想
-
状态定义:
current_max
表示以第i
个元素结尾的最大子段和 -
状态转移:
决策是否将当前元素加入前序子段:
current_max = max(nums[i], current_max + nums[i])
-
全局记录:
持续更新max_sum
记录遍历过程中的最大值
动态规划过程演示(示例数组):
索引 | 元素 | current_max | max_sum | 决策分析 |
---|---|---|---|---|
0 | -2 | -2 | -2 | 初始化 |
1 | 1 | 1 | 1 | 舍弃前序负数,从1重新开始 |
2 | -3 | -2 | 1 | 1 + (-3) > -3 → 保持1 |
3 | 4 | 4 | 4 | 前序和为负,重置起点 |
4 | -1 | 3 | 4 | 4 + (-1) = 3 |
5 | 2 | 5 | 5 | 3 + 2 = 5 |
6 | 1 | 6 | 6 | 5 + 1 = 6(找到最大值) |
7 | -5 | 1 | 6 | 6 + (-5) = 1 |
8 | 4 | 5 | 6 | 1 + 4 = 5 |
💻 C语言完整实现
#include <stdio.h>
/*
* 动态规划求解最大子段和
* 参数说明:
* nums : 整数数组
* numsSize: 数组长度
* 返回值:
* 最大子段和
*/
int maxSubArray(int* nums, int numsSize) {
if (numsSize == 0) return 0; // 处理空数组特例
int current_max = nums[0]; // 当前最大子段和
int max_sum = nums[0]; // 全局最大子段和
for(int i = 1; i < numsSize; i++){
// 决策:延续子段 or 重新开始
current_max = (current_max + nums[i] > nums[i])
? current_max + nums[i]
: nums[i];
// 更新全局最大值
if(current_max > max_sum){
max_sum = current_max;
}
}
return max_sum;
}
int main() {
// 测试用例
int test_case[] = {-2,1,-3,4,-1,2,1,-5,4};
int length = sizeof(test_case)/sizeof(test_case[0]);
int result = maxSubArray(test_case, length);
printf("最大子段和为: %d\n", result); // 输出6
return 0;
}
⚙️ 算法分析
维度 | 说明 |
---|---|
时间复杂度 | O(n),单次遍历数组 |
空间复杂度 | O(1),仅使用两个临时变量 |
适用场景 | 大数据量、需要最优解的场合 |
优势 | 效率高,代码简洁 |
🔍 常见问题解答
Q1:如果数组全为负数怎么办?
例如输入[-3, -1, -2],算法将返回-1,即最大的单个元素值,符合问题定义要求。
Q2:如何记录子数组的起止位置?
可增加变量记录起始和结束索引,当current_max
被重置时更新起始索引,当max_sum
更新时记录结束索引。
Q3:与分治法的区别是什么?
-
分治法时间复杂度O(nlogn)
-
动态规划更适合实际应用场景
📌 应用场景
-
股票买卖时机分析
-
基因组序列分析
-
金融数据分析
-
信号处理
📚 拓展学习
-
《算法导论》第4章 分治策略
-
最大子矩阵和问题(二维扩展)