• 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)

分治策略核心思想:

将原数组划分为左右两部分,最大子数组和可能出现在以下三个位置之一:

  1. 完全在左边子数组
  2. 完全在右边子数组
  3. 跨越中间(包含左子数组的右端 和 右子数组的左端)

所以我们递归地求解左右两边的最大子数组和,并计算跨越中点的最大子数组和,取三者中的最大值即可。


💻 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))


📌 衍生题目建议练习:

  1. LeetCode 53. Maximum Subarray
  2. 最大子矩阵和
  3. 循环数组的最大子数组和
  4. 找出最大子数组的位置(起始和结束索引)

如需我为你讲解 Kadane 算法、或者拓展到最大子矩阵等问题,也可以继续提问哦 😊

1 条评论

  • @ 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