LeetCode #53 最大子序和
题目描述
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
方法一 (暴力法)
/*
遍历每个位置,找出每个位置的最大子序,并与当前最大子序比较
时间复杂度O(n * n)
*/
var maxSubArray = function(nums) {
var max = nums[0]; // 定义初始最大子序
for(var i=0; i<nums.length; i++) {
var sum = 0;
for(var j=i; j<nums.length; j++) {
sum += nums[j];
// 求每个位置的子序和,将和与当前最大子序比较。
if(sum > max) {
max = sum;
}
}
}
return max;
}
方法二 (贪心法)
/*
从起始位置开始累加,对于每个位置,只要累加值大于0,我就贪心!
每个位置选择最佳方案,最终就是最佳方案
并与当前最大子序比较,进行更新
*/
var maxSubArray = function(nums) {
var max = nums[0];
var curSum = nums[0];
for(var i=1; i<nums.length; i++) {
if(curSum > 0) {
// 我的理解是对于当前位置来说,如果前面累加的最大值大于0,那么就拼接上
curSum += nums[i];
} else {
// 否则,从当前位置重新开始计算最大子序
curSum = nums[i];
}
// 更新最大子序
max = Math.max(max, curSum);
}
return max;
}
方法三 (动态规划)
var maxSubArray = function(nums) {
var max = nums[0];
for(var i=1; i<nums.length; i++) {
if(nums[i - 1] > 0) {
// 计算到0-i位置的最大子序值
nums[i] += nums[i-1];
}
max = Math.max(max, nums[i]);
}
return max;
}
方法四 (分治法)
/*
把问题分割成一个个小问题,分别求解,然后把小问题的解组合起来,就是最终解。
这里将数组对半切割,考虑最大子序的位置,可能在左半边数组,也可能在右边数组,也可以跨越左右两边(这时最大子序必然包括数组中间值)
*/
var maxSubArray = function(nums) {
return maxSubArrayHelper(nums, 0, nums.length - 1);
}
/*
注意左右数组的起始值,不能重复计算
*/
var maxSubCrossArray = function(nums, left, mid, right) {
if(left === right) return nums[left];
// 跨越两边时,由于中间值必然存在子序数组中,可以以中间值分半处理
// 处理左半数组
var sum = nums[mid];
var leftMax = nums[mid];
// 左半最大子序,因为位置固定了,求法类似暴力循环
for(var i = mid - 1; i>=left; i--) {
sum += nums[i]; // 累加
if(sum > leftMax) {
leftMax = sum;
}
}
sum = nums[mid + 1];
var rightMax = nums[mid + 1];
for(var i = mid + 2; i<=right; i++) {
sum += nums[i];
if(sum > rightMax) {
rightMax = sum;
}
}
return leftMax + rightMax;
}
var maxSubArrayHelper = function(nums, left, right) {
if(left === right) {
return nums[left];
}
var mid = Math.floor((left + right) / 2); // 中间值
var leftSum = maxSubArrayHelper(nums, left, mid); // 左数组子序
var rightSum = maxSubArrayHelper(nums, mid + 1, right); // 右数组子序
var midSum = maxSubCrossArray(nums, left, mid, right);
return Math.max(leftSum, rightSum, midSum);
}