1. 问题引入:理解最大子数组和
第一次遇到最大子数组和问题时,我正在准备一场重要的技术面试。题目看似简单:给定一个整数数组,找出连续子数组的最大和。但当我真正开始思考解法时,才发现其中蕴含着动态规划的经典思想。
举个例子,对于数组[-2,1,-3,4,-1,2,1,-5,4],最大子数组和是6,对应的子数组是[4,-1,2,1]。这个问题在实际开发中有着广泛应用,比如金融分析中的最大收益区间查找,信号处理中的最大能量区间检测等。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动态规划解法详解
2.1 状态定义与转移方程
动态规划的核心在于状态的定义和转移。在这个问题中,我们定义dp[i]为以第i个元素结尾的所有子数组中的最大和。这个定义非常关键,它保证了我们考虑的子数组一定是连续的。
状态转移方程可以这样推导:
- 如果dp[i-1] > 0,那么将nums[i]加入前面的子数组会更优
- 如果dp[i-1] ≤ 0,那么从nums[i]重新开始一个子数组更优
因此,状态转移方程为:
dp[i] = max(dp[i-1] + nums[i], nums[i])
2.2 边界处理与初始化
在实际编码中,边界条件往往是最容易出错的地方。对于i=0的情况,我们需要特殊处理。有两种常见方法:
- 使用虚拟头节点,初始化dp[0]=0或INT_MIN
- 直接初始化dp[0]=nums[0],然后从i=1开始遍历
在示例代码中,作者选择了第一种方法,添加了一个虚拟节点,这样可以使代码更统一,减少边界条件的判断。
2.3 空间优化技巧
原始实现使用了O(n)的空间存储dp数组,但实际上我们只需要前一个状态的值。因此可以优化为O(1)空间:
cpp复制int maxSubArray(vector<int>& nums) {
int pre = 0, maxAns = nums[0];
for (const auto &x: nums) {
pre = max(pre + x, x);
maxAns = max(maxAns, pre);
}
return maxAns;
}
这种优化在面试中常常被问到,也是考察候选人是否真正理解算法的重要点。
