题目:
(面试题42. 连续子数组的最大和;面试题 16.17. 连续数列 均为改题,要求不同解法)
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
解法:
// Solution One -- 只循环一次,复杂度为O(n),比下面的循环多次要简单方便
var maxSubArray = function(nums) {
let max = nums[0], min = 0, sum = 0
// sum与min之间的差为目前最大值
for(let i = 0,len = nums.length; i < len; i++){
sum += nums[i]
max = Math.max(max, sum-min)
min = Math.min(sum, min)
}
return max
};
// Solution Two -- 循环两次
var maxSubArray = function(nums) {
let len = nums.length
if(len <= 1) return nums
let max = nums[0]
for(let i = 0; i < len; i++){
let count = nums[i]
max = max > count ? max: count
for(let j = i+1; j < len; j++){
count += nums[j]
max = max > count ? max: count
}
}
return max
};
// Solution Three -- 分治法
/*
当最大子数组有 n 个数字时:
若 n==1,返回此元素。
left_sum 为最大子数组前 n/2 个元素,在索引为 (left + right) / 2 的元素属于左子数组。
right_sum 为最大子数组的右子数组,为最后 n/2 的元素。
cross_sum 是包含左右子数组且含索引 (left + right) / 2 的最大值。
*/
var maxSubArray = function(nums){
return maxSubArrayHelper(nums, 0, nums.length - 1)
}
var maxSubArrayHelper = function(nums, left, right){
if(left === right) return nums[left]
let mid = Math.floor((left + right) /2)
let leftMax = maxSubArrayHelper(nums, left, mid),
rightMax = maxSubArrayHelper(nums, mid+1, right),
cross_num = maxSubArrayCrossNum(nums, left, right, mid)
return Math.max(leftMax, cross_num, rightMax)
}
var maxSubArrayCrossNum = function(nums, left, right, mid){
if(left === right) return nums[left]
let count = 0,leftSubNum = rightSubNum = Number.MIN_SAFE_INTEGER
for(let i = mid; i > left - 1; --i){
count += nums[i]
leftSubNum = Math.max(count, leftSubNum)
}
count = 0
for(let j = mid + 1; j < right + 1; ++j){
count += nums[j]
rightSubNum = Math.max(count, rightSubNum)
}
return leftSubNum + rightSubNum
}
// Solution Four -- 动态规划
/*
以子序列的结束节点为基准,先遍历出以某个节点为结束的所有子序列,因为每个节点都可能
会是子序列的结束节点,因此要遍历下整个序列
*/
// 时间最快,内存最小
var maxSubArray3_1 = function(nums){
// 在每一个扫描点计算以改点数值为结束点的子数列的最大和
let max_ending_here = max_so_far = nums[0]
for(let i = 1; i < nums.length; i++){
// 以每个位置为重点的最大子序列,都是基于前一位置的最大子数列计算得出
max_ending_here = Math.max(nums[i], max_ending_here + nums[i])
max_so_far = Math.max(max_so_far, max_ending_here)
}
return max_so_far
}
// ===> 可以转化为下列形式
var maxSubArray3_2 = function(nums){
let res = nums[0], sum = 0
for(let num of nums){
sum = sum+num > num ? sum+num : num
res = Math.max(res, sum)
}
return res
}
// 官方还提供了一种 贪心算法 思路,现在还不知道怎么理解,包括动态规划也是刚入脑袋不久的词,希望有时间可以补充