刷题打卡:数组操作の最大子数组和

110 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情

一、题目描述:

最大子数组和

给你一个整数数组 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 <= nums[i] <= 104

二、思路与实现:

思路一:采用贪心算法:

首先对数组进行遍历,当前最大连续子序列和为 sum,结果为 ans

  • 如果 sum > 0,则说明 sum 对结果有增益效果,则 sum 保留并加上当前遍历数字

  • 如果 sum <= 0,则说明 sum 对结果无增益效果,需要舍弃,则 sum 直接更新为当前遍历数字

每次比较 sum 和 ans的大小,将最大值置为ans,遍历结束返回结果;

时间复杂度:O(n)

代码实现:

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function(nums) {
    let ans = nums[0];
    let sum = 0;
    for(const num of nums) {
        if(sum > 0) {
            sum += num;
        } else {
            sum = num;
        }
        ans = Math.max(ans, sum);
    }
    return ans;
};。

思路二:分治算法

不断分割,直到每个部分是一个数字为止,然后不断合并,返回左右和左右合并之后,3个最大子序和中的最大的一个

复杂度:时间复杂度O(nlogn),二分复杂度O(logn),二分之后每一层统计左右和合并之后的最大子序和复杂度是O(n),所以之后的复杂度是O(nlogn)。空间复杂度O(logn),递归的栈空间,因为是二分,每次数据规模减半

function crossSum(nums, left, right, mid) {
    if (left === right) {//左右相等 返回左边的值
        return nums[left];
    }

    let leftMaxSum = Number.MIN_SAFE_INTEGER;//左边最大值初始化
    let leftSum = 0;
    for (let i = mid; i >= left; --i) {
        leftSum += nums[i];
        leftMaxSum = Math.max(leftMaxSum, leftSum);//更新左边最大子序和
    }

    let rightMaxSum = Number.MIN_SAFE_INTEGER;
    let rightSum = 0;
    for (let i = mid + 1; i <= right; ++i) {
        rightSum += nums[i];
        rightMaxSum = Math.max(rightMaxSum, rightSum);//更新右边最大子序和
    }

    return leftMaxSum + rightMaxSum;//返回左右合并之后的最大子序和
}

function _maxSubArray(nums, left, right) {
    if (left === right) {//递归终止条件
        return nums[left];
    }

    const mid = Math.floor((left + right) / 2);
    const lsum = _maxSubArray(nums, left, mid);//左边最大子序和
    const rsum = _maxSubArray(nums, mid + 1, right);//右边最大子序和
    const cross = crossSum(nums, left, right, mid);//合并左右的之后的最大子序和

    return Math.max(lsum, rsum, cross);//返回3中子序和中最大的
}

var maxSubArray = function(nums) {
    return _maxSubArray(nums, 0, nums.length - 1);
};

三、总结:

对于本题贪心的理解,可以这样想:1.假如全是负数,那就是找最大值即可,因为负数肯定越加越小。 2.如果有正数,则肯定从正数开始计算和,不然前面有负值,和肯定变小了,所以从正数开始。 3.当和小于零时,这个区间就告一段落了,然后从下一个正数重新开始计算(也就是又回到 2 了)。

贪心好理解一些,分治算法稍难一些~