53. Maximum Subarray

本文探讨了寻找连续数组中具有最大和的子数组问题。介绍了暴力解法、分治算法和动态规划三种方法,并详细解释了动态规划的实现过程及代码示例。

标签(空格分隔): leetcode



    1. 原题
    2. 分析:
      题目要求在一个连续数组中找到一个子数组,是的子数组的元素的和最大。

      • 可以使用暴力解答, n 个数一共有 C2n 种选择,分别计算这 C2n 种选择的结果然后求最大值,但是时间复杂度 O(n2) ,这道题不通过。。
      • 可以使用分治算法:

      >

      1. 假设数组A的low, mid, high分别表示数组相应的坐标,然后,数组的最大子数组有3种情况,也就是
        子数组在A[low,mid]中,
        子数组在A[mid+1, high]中,
        子数组在A[low, high]中
      2. 然后在上面那三种情况中,继续可以分为同样的3种情况,也就是子问题和原问题有一样的结构。
      3. 在这种情况下,时间复杂度有主定理:
        T(n)=aT(n/b)+f(n)
        这里将原问题分解为 2 个 n/2 的问题
        时间复杂度 O(nlgn)
        但是。。。。。这个在leetcode这道题上是超时的。。

      • 使用动态规划:
        如果用函数 f(i)表示以第 i个数字结尾的子数组的最大和,那么我们需要求出 max[f(i)],其中 0 <= i < n。我们可用如下边归公式求 f(i):

    f(i)={nums[i]f[i1]+nums[i]i=0orf[i1]<0i>0andf[i1]>0

    这个公式的意义:当以第 i-1 个数字结尾的子数组中所有数字的和小于 0 时,如果把这个负数与第 i 个数累加,得到的结果比第 i 个数字本身还要小,所以这种情况下以第 i 个数字结尾的子数组就是第 i 个数字本身。如果以第 i-1 个数字结尾的子数组中所有数字的和大于 0,与第 i 个数字累加就得到以第 i 个数字结尾的子数组中所有数字的和。
    还要考虑特殊情况全是负号的情况就可以

    1. 代码:
      • 动态规划:
    class Solution {
    public:
    
        int maxSubArray(vector<int>& nums) {
    
            int MaxSum = 0;
            int CurSum = 0;
            bool hasPositive = false;
            int max = INT_MIN;
            for (int i = 0; i < nums.size(); i++){
                if (nums[i] > 0){
                    hasPositive = true;
                    break;
                }
                if (nums[i] > max){
                    max = nums[i];
                }
            }
            if (!hasPositive){
                return max;
            }
    
            for (int i = 0; i < nums.size(); i++){
                CurSum += nums[i];
                if (CurSum > MaxSum){
                    MaxSum = CurSum;
                }
                if (CurSum < 0){
                    CurSum = 0;
                }
            }
            return MaxSum;
        }
    
    };
    • 分治的(超时)
    #include <iostream>
    #include <vector>
    #include <climits>
    #include <cmath>
    
    using namespace std;
    
    struct DATA {
        int low;
        int high;
        int sum;
        DATA(int low, int high, int sum) {
            this->low = low;
            this->high = high;
            this->sum = sum;
        }
    };
    
    DATA findMaxCrossSum(vector<int> A, int low, int mid, int high) {
        int left_sum = INT_MIN;
        int sum = 0;
        int max_left = -1;
        for (int i = mid; i >= low; i--) {
            sum += A[i];
            if (sum > left_sum) {
                left_sum = sum;
                max_left = i;
            }
        }
        int right_sum = INT_MIN;
        int max_right = -1;
        sum = 0;
        for (int i = mid + 1; i <= high; i++) {
            sum += A[i];
            if (sum > right_sum) {
                right_sum = sum;
                max_right = i;
            }
        }
        return DATA(max_left, max_right, left_sum + right_sum);
    
    }
    DATA findMaxSub(vector<int> A, int low, int high) {
        if (high == low) {
            return DATA(low, low, A[low]);
    
        }
        else {
            int mid = (low + high) / 2;
            DATA left = findMaxSub(A, low, mid);
            DATA right = findMaxSub(A, mid + 1, high);
            DATA cross = findMaxCrossSum(A, low, mid, high);
            if (left.sum >= right.sum && left.sum >= cross.sum)
                return left;
            if (right.sum >= left.sum && right.sum >= cross.sum)
                return right;
            return cross;
        }
    }
    
    
    int maxSubArray(vector<int>& nums) {
        int sum = 0;
        bool hasNegative = false;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] < 0)
                hasNegative = true;
            sum += nums[i];
        }
        if (!hasNegative)
            return sum;
        DATA ans = findMaxSub(nums, 0, nums.size() - 1);
        return ans.sum;
    }
    
    
    int main() {
        vector<int> v{ -2,1,-3,4,-1,2,1,-5,4 };
        cout << maxSubArray(v) << endl;
        system("pause");
        return 0;
    }
    ### 最大子数组和算法实现与解释 最大子数组和问题旨在从一个整数数组中找出具有最大和的连续子数组。该问题的经典解法采用动态规划思想,其核心逻辑在于逐个位置计算以当前元素结尾的最大子数组和,并记录全局最大值。 动态规划状态转移方程如下: `maxSubArray(A, i) = maxSubArray(A, i - 1) > 0 ? maxSubArray(A, i - 1) : 0 + A[i]` 该公式表示如果前一个位置的最大子数组和大于零,则将其加入当前位置的数值形成新的候选值;否则仅保留当前位置的数值作为起始点。通过这种方式,可以在线性时间内完成整个数组的遍历并求出最大子数组和[^1]。 #### Java 实现示例 ```java class Solution { public int maxSubArray(int[] A) { int n = A.length; int[] dp = new int[n]; // dp[i] 表示以 A[i] 结尾的最大子数组和 dp[0] = A[0]; int max = dp[0]; for (int i = 1; i < n; i++) { dp[i] = A[i] + (dp[i - 1] > 0 ? dp[i - 1] : 0); max = Math.max(max, dp[i]); } return max; } } ``` 上述代码使用了一个长度为 `n` 的数组 `dp` 来存储每个位置上的最大子数组和,最终返回全局最大值 `max`。该方法的时间复杂度为 O(n),空间复杂度也为 O(n)。 为了进一步优化空间复杂度,可以仅维护前一个状态的值,从而将空间复杂度降低至 O(1): #### 空间优化版本(O(1)) ```java class Solution { public int maxSubArray(int[] nums) { if (nums == null || nums.length == 0) { return 0; } int prev = nums[0]; int result = prev; for (int i = 1; i < nums.length; ++i) { prev = Math.max(nums[i], prev + nums[i]); result = Math.max(result, prev); } return result; } } ``` 在该实现中,变量 `prev` 记录以当前元素结尾的最大子数组和,而 `result` 跟踪整个过程中的最大值。这样避免了额外数组的使用,使空间效率更优[^2]。 --- ### 相关应用场景 该算法不仅适用于最大子数组问题本身,还可推广至股票买卖利润最大化等场景。例如,在给定每日股价数组中寻找最大收益时,可以通过构造价格差分数组并应用最大子数组和算法来解决[^3]。 --- ### 示例:C++ 实现(异常安全与空间优化) 以下是一个 C++ 版本的实现,结合了内存管理优化和异常处理机制,确保程序在面对大规模输入时具备更好的鲁棒性: ```cpp #include <iostream> #include <vector> #include <algorithm> int maxSubArraySum(const std::vector<int>& nums) { if (nums.empty()) return 0; int prev = nums[0]; int result = prev; for (size_t i = 1; i < nums.size(); ++i) { prev = std::max(nums[i], prev + nums[i]); result = std::max(result, prev); } return result; } int main() { try { std::vector<int> nums; nums.reserve(1 << 20); // 预留百万级元素的空间 for (int i = 0; i < 1000000; ++i) { nums.push_back(i % 100 - 50); // [-50, 49] } int maxSum = maxSubArraySum(nums); std::cout << "Maximum subarray sum: " << maxSum << std::endl; } catch (const std::bad_alloc& e) { std::cerr << "Memory allocation failed: " << e.what() << std::endl; } catch (...) { std::cerr << "An unexpected error occurred." << std::endl; } return 0; } ``` 此实现通过 `reserve()` 提前分配足够容量,减少频繁扩容带来的性能开销,并通过 `try-catch` 捕获可能的内存分配异常,提高程序稳定性[^1]。 ---
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值