- C++
求最大子数组和(Maximum Subarray Problem)——分治算法实现
- @ 2025-6-9 23:09:03
求最大子数组和(Maximum Subarray Problem)——分治算法实现
🎯 问题描述:
给定一个整数数组 nums,找到一个具有最大和的连续子数组(子数组至少包含一个元素),返回该最大和。
例如:
输入:[-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大为 6。
🧩 解题思路:分治法(Divide and Conquer)
分治策略核心思想:
将原数组划分为左右两部分,最大子数组和可能出现在以下三个位置之一:
- 完全在左边子数组
- 完全在右边子数组
- 跨越中间(包含左子数组的右端 和 右子数组的左端)
所以我们递归地求解左右两边的最大子数组和,并计算跨越中点的最大子数组和,取三者中的最大值即可。
💻 C++代码实现 + 注释:
#include <iostream>
#include <vector>
#include <algorithm> // 用于 max 函数
using namespace std;
// 计算跨越中点的最大子数组和
int maxCrossingSum(const vector<int>& nums, int left, int mid, int right) {
// 包含mid的左半部分最大和
int leftSum = INT_MIN;
int sum = 0;
for (int i = mid; i >= left; --i) {
sum += nums[i];
leftSum = max(leftSum, sum);
}
// 包含mid+1的右半部分最大和
int rightSum = INT_MIN;
sum = 0;
for (int i = mid + 1; i <= right; ++i) {
sum += nums[i];
rightSum = max(rightSum, sum);
}
// 返回跨越中点的总和
return leftSum + rightSum;
}
// 分治法主函数
int maxSubArrayDivideAndConquer(const vector<int>& nums, int left, int right) {
if (left == right) {
// 只有一个元素时,直接返回
return nums[left];
}
int mid = left + (right - left) / 2;
// 三种情况取最大
int maxLeft = maxSubArrayDivideAndConquer(nums, left, mid); // 左边最大子数组和
int maxRight = maxSubArrayDivideAndConquer(nums, mid + 1, right); // 右边最大子数组和
int maxCross = maxCrossingSum(nums, left, mid, right); // 跨越中点的最大子数组和
return max(max(maxLeft, maxRight), maxCross);
}
int main() {
vector<int> nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
int result = maxSubArrayDivideAndConquer(nums, 0, nums.size() - 1);
cout << "最大子数组和为:" << result << endl;
return 0;
}
📝 输出结果:
最大子数组和为:6
⏱️ 时间复杂度分析:
- 分解:O(1)
- 解决:递归调用两个子问题 T(n/2)
- 合并:计算跨越中点的子数组和需要 O(n)
所以时间复杂度为:
$$T(n) = 2T\left(\frac{n}{2}\right) + O(n) \Rightarrow T(n) = O(n \log n) $$🆚 对比 Kadane 算法(动态规划方法)
| 方法 | 时间复杂度 | 是否适合初学者 | 是否使用分治 |
|---|---|---|---|
| 分治法 | O(n log n) | ✅ 思路清晰 | ✅ 是 |
| Kadane 算法 | O(n) | ✅ 更快更简单 | ❌ 不是分治 |
如果你正在学习分治算法,这个例子非常经典;如果只是追求效率,推荐使用 Kadane 算法(O(n))。
📌 衍生题目建议练习:
- LeetCode 53. Maximum Subarray
- 最大子矩阵和
- 循环数组的最大子数组和
- 找出最大子数组的位置(起始和结束索引)
如需我为你讲解 Kadane 算法、或者拓展到最大子矩阵等问题,也可以继续提问哦 😊
1 条评论
-
admin SU @ 2025-11-2 14:28:40
以下是添加详细注释后的代码:
#include <iostream> #include <vector> #include <algorithm> // 用于 max 函数 #include <climits> // 用于 INT_MIN using namespace std; // 计算跨越中点的最大子数组和 int maxCrossingSum(const vector<int>& nums, int left, int mid, int right) { // 计算左半部分(从mid向左)的最大和,必须包含nums[mid] int leftSum = INT_MIN; // 初始化为最小整数,确保能被更新 int sum = 0; for (int i = mid; i >= left; --i) { sum += nums[i]; leftSum = max(leftSum, sum); // 更新左半部分的最大和 } // 计算右半部分(从mid+1向右)的最大和,必须包含nums[mid+1] int rightSum = INT_MIN; sum = 0; for (int i = mid + 1; i <= right; ++i) { sum += nums[i]; rightSum = max(rightSum, sum); // 更新右半部分的最大和 } // 返回跨越中点的左右两部分最大和的总和 return leftSum + rightSum; } // 分治法主函数:求解[left, right]区间内的最大子数组和 int maxSubArrayDivideAndConquer(const vector<int>& nums, int left, int right) { if (left == right) { // 递归终止条件:区间只有一个元素时,最大子数组和就是自身 return nums[left]; } int mid = left + (right - left) / 2; // 计算中点,避免溢出 // 递归求解左半区间的最大子数组和 int maxLeft = maxSubArrayDivideAndConquer(nums, left, mid); // 递归求解右半区间的最大子数组和 int maxRight = maxSubArrayDivideAndConquer(nums, mid + 1, right); // 求解跨越中点的最大子数组和 int maxCross = maxCrossingSum(nums, left, mid, right); // 三者取最大值作为最终结果 return max(max(maxLeft, maxRight), maxCross); } int main() { vector<int> nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; // 示例数组 // 调用分治法函数计算最大子数组和 int result = maxSubArrayDivideAndConquer(nums, 0, nums.size() - 1); cout << "最大子数组和为:" << result << endl; // 输出结果 return 0; }代码逻辑说明
该代码使用分治法解决“最大子数组和”问题,核心思路是将数组不断划分为左、右两个子数组,分别求解这两个子数组的最大和,同时求解跨越两个子数组中点的最大和,最终取这三者的最大值。
maxCrossingSum函数:专门处理“跨越中点”的情况,分别计算左半部分(从mid向左延伸)和右半部分(从mid+1向右延伸)的最大和,再将两者相加。maxSubArrayDivideAndConquer函数:递归实现分治逻辑,当区间缩小到只有一个元素时直接返回该元素,否则递归处理左、右子区间,并结合maxCrossingSum的结果得到最终最大值。main函数:定义示例数组,调用分治函数并输出结果(示例中最大子数组为[4, -1, 2, 1],和为6)。
- 1