创见博客
最大子数组和
七崽爱吃小饼干2026/01/17阅读 2专栏 算法合集

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组是数组中的一个连续部分。

示例 1:

codeType
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:

codeType
输入:nums = [1]
输出:1

示例 3:

codeType
输入:nums = [5,4,-1,7,8]
输出:23

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104

进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。

解法

解法一:滑动窗口
ts
function maxSubArray(nums: number[]): number {
    // 可以用滑动窗口解决。
    // 如果纳入下个数,导致窗口和小于0,就直接抛弃整个窗口
    // 如果下个数是正数,则无条件添加,这题并不需要得到序列,所以其实不需要slow
    let slow = -1, fast = -1
    let sum = 0
    let res = -Infinity
    const n = nums.length
    while(fast + 1 < n){
        if(sum < 0){ // 防止数组中只有小于0的数字
            // 只会有一个元素
            fast++
            slow++
            sum = nums[fast]
        }else if(sum + nums[fast + 1] < 0){
            // 抛弃整个窗口
            fast++
            slow = fast
            sum = nums[fast]
        }else if(sum + nums[fast + 1] >= 0){
            // 下个数纳入后仍窗口和大于0
            // 直接划入窗口
            fast++
            sum += nums[fast]
        }
        res = Math.max(res, sum)
    }
    return res
};
解法二:分治法
分治法的时间复杂度和空间复杂度并不比方法一快,空间复杂度也需要O(nlogn)。但是该结果栈可以保存下来,从而在求任意区间时以O(logn)的复杂度就可以求解。
ts
function maxSubArray(nums: number[]): number {
    // 分治法
    // 整个序列自顶向下逐个拆解成左右区间
    // 直到区间被拆解成1个数字
    /**
        对每个区间维护四个值
        lSum 表示 [l,r] 内以 l 为左端点的最大子段和
        rSum 表示 [l,r] 内以 r 为右端点的最大子段和
        mSum 表示 [l,r] 内的最大子段和
        iSum 表示 [l,r] 的区间和
     */
     const helper = (l: number, r: number): {
        lSum: number
        rSum: number
        mSum: number
        iSum: number
     } => {
        if(l === r){
            // 区间只有一个元素时
            return {
                lSum: nums[l],
                rSum: nums[l],
                mSum: nums[l],
                iSum: nums[l],
            }
        }
        const mid = Math.floor((l + r) / 2)
        const lSub = helper(l, mid) // 求左边序列的四个状态
        const rSub = helper(mid + 1, r) // 求右边序列的四个状态

        // 合并后的序列的左端最大和 = max(左序列的左端最大和, 左序列的总和 + 右序列的左端最大和)右端序列最大和同理
        const lSum = Math.max(lSub.lSum, lSub.iSum + rSub.lSum)
        const rSum = Math.max(rSub.rSum, rSub.iSum + lSub.rSum)
        // 序列总和
        const iSum = lSub.iSum + rSub.iSum
        // 最大字段和原理如上图
        const mSum = Math.max(Math.max(lSub.mSum, rSub.mSum), lSub.rSum + rSub.lSum);
        return {
            lSum,
            rSum,
            iSum,
            mSum
        }
     }
     return helper(0, nums.length - 1).mSum
};
解法三 动态规划
ts
var maxSubArray = function(nums) {
    let pre = 0, maxAns = nums[0];
    nums.forEach((x) => {
        pre = Math.max(pre + x, x);
        maxAns = Math.max(maxAns, pre);
    });
    return maxAns;
};
评论
0/100