LeetCode-Maximum Subarray

本文探讨了最大子序列和问题的两种解决方法:动态规划和分治法。动态规划方法通过一次遍历找到最大子序列和,而分治法则通过递归方式处理序列的不同部分。文中还提供了详细的代码实现。

最大子序列和问题

1. 题目描述

Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
Example given the array [-2,1,-3,4,-1,2,1,-5,4],
the contiguous subarray [4,-1,2,1] has the largest sum = 6.


2. 解题思路

一开始觉得和滑动窗口一个问题,但是想不到一个终止的条件,然后发现只要是前面序列和小于0,就设置sum=0。

1. 直接求解O(n)-动态规划

maxSubArray(A, i) = maxSubArray(A, i - 1) >0 ? maxSubArray(A, i - 1) : 0 + A[i];

遍历一次数组,sum+=A[i],如果sum大于result,更新result,如果sum小于0,更新sum为0.

2. 利用分治的方法O(nlogn)

对于一数字序列,其最大连续子序列和对应的子序列可能出现在三个地方。

  • 整个出现在输入数据的前半部(左)
  • 整个出现在输入数据的后半部(右)
  • 跨越输入数据的中部从而占据左右两半部分。

前两种情况可以通过递归求解,第三种情况可以通过求出前半部分的最大和(包含前半部分的最后一个元素)以及后半部分的最大和(包含后半部分的第一个元素)而得到,然后将这两个和加在一起即可。
T(1) = 1
T(N) = 2T(N/2) + O(N)

3.代码

先贴一波分治的代码

public static int maxSubsequenceSum(int[] a, int left, int right) {
    if(left == right) { //Base case
        if(a[left] > 0) {
            return a[left];
        } else {
            return 0; //保证最小值为0
        }
    }

    int center = (left+right)/2;
    int maxLeftSum = maxSubsequenceSum(a, left, center); //递归调用,求左部分的最大和
    int maxRightSum = maxSubsequenceSum(a, center+1, right);//递归调用,求右部分的最大和

    int leftBorderSum = 0, maxLeftBorderSum = 0;//定义左边界子序列的和
    for(int i=center; i>=left; i--) {//求左边界的最大和(从右边开始往左求和)
        leftBorderSum += a[i];
        if(leftBorderSum > maxLeftBorderSum) {
            maxLeftBorderSum = leftBorderSum;
        }
    }

    int rightBorderSum = 0, maxRightBorderSum = 0;//定义右边界子序列的和
    for(int i=center+1; i<=right; i++) {//求右边界的最大和(从左边开始往右求和)
        rightBorderSum += a[i];
        if(rightBorderSum > maxRightBorderSum) {
            maxRightBorderSum = rightBorderSum;
        }
    }

    //选出这三者中的最大值并返回(max3(int a, int b, int c)的实现没有给出)
    return max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum);
}

接下来就是动态规划的代码了

class Solution{
    public:
     int maxSubArray(vector<int>& nums) {
         int largest_num= nums.at(0);
         int sum=0;
         for(int i=0; i < nums.size(); i++) {
            sum+=nums.at(i);
            if(sum> largest_num) {
                largest_num=sum;
            } 
             if(sum < 0) 
              sum= 0;
         }
         return largest_num;
    }
};

表达比较明确,但是还不够短,discussion里面说到,可以把for循环里面的语句变成两条语句。
sum=max(sum+nums.at(i),A[i]);
largest_num=max(largest_num,sum);
感叹大佬的水平(膜)。

你提供的代码是 **LeetCode 第53题:最大子数组和(Maximum Subarray)** 的标准动态规划解法,使用了 **Kadane 算法**,是一种非常高效的一维动态规划算法。它可以在 **O(n)** 时间复杂度内找到连续子数组的最大和。 --- ## ✅ 代码解析与完整 VS2022 可运行版本 以下是适合在 **Visual Studio 2022 (VS2022)** 中运行的完整代码版本,包含 `vector`、主函数和测试用例。 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; class Solution { public: 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; } }; // 主函数测试 int main() { Solution sol; vector<int> nums; // 测试用例 1 nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; cout << "输入数组: "; for (int num : nums) cout << num << " "; cout << "\n最大子数组和为: " << sol.maxSubArray(nums) << endl << endl; // 测试用例 2 nums = {1}; cout << "输入数组: "; for (int num : nums) cout << num << " "; cout << "\n最大子数组和为: " << sol.maxSubArray(nums) << endl << endl; // 测试用例 3 nums = {5, 4, -1, 7, 8}; cout << "输入数组: "; for (int num : nums) cout << num << " "; cout << "\n最大子数组和为: " << sol.maxSubArray(nums) << endl << endl; // 测试用例 4 nums = {-1, -2, -3, -4}; cout << "输入数组: "; for (int num : nums) cout << num << " "; cout << "\n最大子数组和为: " << sol.maxSubArray(nums) << endl << endl; return 0; } ``` --- ## ✅ 示例输出 ``` 输入数组: -2 1 -3 4 -1 2 1 -5 4 最大子数组和为: 6 输入数组: 1 最大子数组和为: 1 输入数组: 5 4 -1 7 8 最大子数组和为: 23 输入数组: -1 -2 -3 -4 最大子数组和为: -1 ``` --- ## ✅ 算法逻辑详解 ### ✅ 问题背景 给定一个整数数组 `nums`,找到一个具有最大和的连续子数组(子数组至少包含一个元素),返回其最大和。 ### ✅ 解法思路:Kadane 算法(贪心 + 动态规划) #### 核心思想: - 维护一个变量 `pre`,表示以当前元素结尾的最大子数组和。 - 对于每个元素 `x`: - 如果 `pre + x` 比 `x` 大,说明可以继续扩展当前子数组; - 否则,舍弃之前的子数组,从当前元素重新开始。 - 每次更新全局最大值 `maxAns`。 #### 示例说明: ``` nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4] 遍历过程: pre = -2 → maxAns = -2 pre = max(-2 + 1, 1) = 1 → maxAns = 1 pre = max(1 + (-3), -3) = -2 → maxAns = 1 pre = max(-2 + 4, 4) = 4 → maxAns = 4 ... 最终 maxAns = 6 ``` --- ## ✅ 时间与空间复杂度 | 类型 | 复杂度 | 说明 | |------|--------|------| | 时间复杂度 | O(n) | 只遍历一次数组 | | 空间复杂度 | O(1) | 只使用常数级额外空间 | --- ## ✅ 常见问题排查(VS2022) 1. **编译错误** - 确保包含 `<vector>` 和 `<algorithm>` - 使用 `using namespace std;` 或加上 `std::` 前缀 2. **运行时错误** - 注意空数组处理(虽然题目保证至少一个元素) - 确保 `max()` 函数参数正确 3. **逻辑错误** - 初始化 `maxAns` 为 `nums[0]` 是关键 - `pre` 的初始值应为 `0` --- ## ✅ 对比其他解法 | 解法 | 时间复杂度 | 空间复杂度 | 特点 | |------|------------|------------|------| | Kadane 算法(当前方法) | O(n) | O(1) | 最优解,推荐 | | 暴力枚举所有子数组 | O(n²) | O(1) | 简单但低效 | | 分治法(归并思想) | O(n log n) | O(log n) | 面试拓展思路 | | 动态规划(dp 数组) | O(n) | O(n) | 易理解,但空间略高 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值