剑指offer_42_连续子数组的最大和【javascript】

50 阅读3分钟

题目链接

leetcode.cn/problems/li…

题目

输入一个整型数组,数组里🈶️正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。

示例1:

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

提示:

题解

遍历

  • 时间复杂度O(n)
  • 空间复杂度O(1)
/**
 * @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;
    }
    // console.log(num, sum);
    ans = Math.max(ans, sum);
  }
  return ans;
}

贪心算法

  • 时间复杂度O(n)
  • 空间复杂度O(1)

从左向右迭代,一个个数字加过去,若果sum < 0, 重新开始找子序串

var maxSubArray = function(nums) {
  let result = nums[0];
  let sum = 0;
  for(const num of nums) {
    sum += num;
    result = Math.max(result, sum);
    if (sum < 0) {
      sum = 0;
    }
    console.log(num, sum);
  }
  return result;
}

动态规划

  • 时间复杂度O(n)
  • 空间复杂度O(1)

f(i)的取值为:

  • f(i - 1) <= 0时,取data[i]
  • f(i - 1) > 0时,取f(i - 1) + data[i]
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;
};

分治

  • 时间复杂度O(nlog(n))
  • 空间复杂度O(log(n)) 因为调用栈的深度最多是logn

leetcode-cn.com/problems/ma…

分治法的做题思路是:先将问题分解为子问题;解决子问题后,再将子问题合并,解决主问题。 我们把数组nums以中间位置(m)分为左(left)右(right)两部分. 那么有, left = nums[0]...nums[m - 1] 和 right = nums[m + 1]...nums[n-1]
最大子序列和的位置有以下三种情况:

  • 考虑中间元素nums[m], 跨越左右两部分,这里从中间元素开始,往左求出后缀最大,往右求出前缀最大, 保持连续性。
  • 不考虑中间元素,最大子序列和出现在左半部分,递归求解左边部分最大子序列和
  • 不考虑中间元素,最大子序列和出现在右半部分,递归求解右边部分最大子序列和
    分别求出三种情况下最大子序列和,三者中最大值即为最大子序列和。
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;
}

/**
 * @param {number[]} nums
 * @param {number} left
 * @param {number} right
 * @return {number}
 */
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);
}

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function(nums) {
    return __maxSubArray(nums, 0, nums.length - 1);
};

暴力

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)
/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function(nums) {
    let numsLength = nums.length;
    if(!numsLength) return; 
    let max = nums[0];
    for(let i = 0; i < numsLength; i++) {
        let sum = 0;
        for (let j = i; j < numsLength; j++) {
            sum += nums[j];
            if (sum > max) {
                max = sum;
            }
        }
    }
    return max;
};