算法(TS):最大子序和

最大子序和

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

示例 1:

输入:nums = -2,1,-3,4,-1,2,1,-5,4

输出:6

解释:连续子数组 4,-1,2,1 的和最大,为 6 。

示例 2:

输入:nums = 1

输出:1

示例 3:

输入:nums = 5,4,-1,7,8

输出:23

提示:

  • 1 <= nums.length <= 105
  • -104 <= numsi <= 104

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

解法一

使用分治算法求解最大子序和问题的思路是将数组分成两半,分别求解左半部分的最大子序和、右半部分的最大子序和,以及跨越中点的最大子序和。这三个值中的最大值即为整个数组的最大子序和。

ini 复制代码
function maxSubArray(nums: number[]): number {
    if (nums.length === 0) {
        return 0;
    }

    return divideAndConquer(nums, 0, nums.length - 1);
}

function divideAndConquer(nums: number[], left: number, right: number): number {
    if (left === right) {
        return nums[left]; // 单个元素的最大子序和就是该元素本身
    }

    const mid = Math.floor((left + right) / 2);

    // 分别求解左半部分、右半部分的最大子序和
    const leftMax = divideAndConquer(nums, left, mid);
    const rightMax = divideAndConquer(nums, mid + 1, right);

    // 求解跨越中点的最大子序和
    const crossMax = maxCrossingSum(nums, left, mid, right);

    // 返回三者中的最大值
    return Math.max(leftMax, rightMax, crossMax);
}

function maxCrossingSum(nums: number[], left: number, mid: number, right: number): number {
    let leftSum = -Infinity;
    let sum = 0;

    // 从中点向左计算最大子序和
    for (let i = mid; i >= left; i--) {
        sum += nums[i];
        leftSum = Math.max(leftSum, sum);
    }

    let rightSum = -Infinity;
    sum = 0;

    // 从中点向右计算最大子序和
    for (let i = mid + 1; i <= right; i++) {
        sum += nums[i];
        rightSum = Math.max(rightSum, sum);
    }

    // 返回跨越中点的最大子序和
    return leftSum + rightSum;
}

// 示例
const nums: number[] = [-2, 1, -3, 4, -1, 2, 1, -5, 4];
console.log(maxSubArray(nums)); // 输出: 6 (子数组 [4, -1, 2, 1] 的和最大)

算法的时间复杂度为 O(n log n),由于把数组分成两半之后递归,因此空间复杂度为O(log n)。

解法二

运用动态规划求解。在遍历数组的过程中,判断 numsi 的值是否应该并入之前的序列 prev 中,如果 numsi > prev + numsi,意味着 numsi 应该单独成为一个序列,否则该并入 prev 中形成一个比自己更大的序列。maxAn 则是从所有的序列和中取最大值。

ini 复制代码
function maxSubArray(nums: number[]): number {
    let maxAn = nums[0],prev = nums[0],i = 1
    const len = nums.length
    while(i < len) {
        prev = Math.max(prev + nums[i],nums[i])
        maxAn = Math.max(maxAn,prev)
        i++
    }
    return maxAn
};

时间复杂度O(n),空间复杂度O(1)。这种方法避免了递归调用和栈空间的开销,将分治算法转化为迭代的形式,更容易理解和实现。

相关推荐
爱刷碗的苏泓舒26 分钟前
PPP-AR 中的参考星选取:数学原理、评价指标与切换处理
算法·gnss·模糊度固定·ppp-ar·星间单差·参考星·卫星端偏差
xqslsm1 小时前
TypeScript 联合类型深入解析
typescript
叫我少年3 小时前
TypeScript 基础类型
typescript
烬羽3 小时前
递归老写崩?一个"退回"公式,把回溯题变成填空题
javascript·深度学习·算法
闪电悠米3 小时前
力扣hot100-41.缺失的第一个正数-原地哈希详解
数据结构·算法·哈希算法
星空露珠4 小时前
28种颜色对应名称,
开发语言·数据库·算法·游戏·lua
QXWZ_IA6 小时前
桥梁数字孪生怎么落地?
人工智能·科技·算法·智能硬件·政务
Kel6 小时前
输出层与反分词(Output Layer & Detokenization)
人工智能·算法·架构
大爱编程♡6 小时前
Vue3+TypeScript+element-plus的文章管理项目(配后端接口)-退出及文章管理页面
前端·javascript·typescript
陕西企来客6 小时前
2026年7月技术好GEO优化方案:算法适配与内容策略
人工智能·算法·机器学习·技术好geo优化