53. 最大子序和
题目描述: 给定一个整数数组 nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
- 动态规划or贪心:
容易想到的实现方法,使用一个数组记录连续子序列的和,选择子序列的标准:如果上一个子序列的和与当前值相加后反而比当前值还小,那就舍弃上一个子序列,而从当前值开始一个新的子序列计算和,一次遍历,时间复杂度O(n)
//C int maxSubArray(int* nums, int numsSize){ if(numsSize == 1) return nums[0]; int *resArr = (int *)malloc(numsSize * sizeof(int)); int i, temp; resArr[0] = nums[0]; for(i = 1; i < numsSize; i++){ temp = (nums[i] + resArr[i - 1]); resArr[i] = nums[i] > temp ? nums[i] : temp; } temp = resArr[0]; for(i = 1; i < numsSize; i++){ if(resArr[i] > temp) temp = resArr[i]; } return temp; } //JS var maxSubArray = function(nums) { let sumArr = [nums[0]], tmp = 0; for(let i = 1; i < nums.length; i++){ tmp = sumArr[i - 1]; sumArr.push(Math.max(nums[i], nums[i] + tmp)); } return Math.max.apply(null, sumArr); };
- 进一步提高空间复杂度的做法,使用变量记录最大自序和
//JS var maxSubArray = function(nums) { let res = sum = nums[0]; for(let i = 1; i < nums.length; i++){ sum = Math.max(sum + nums[i], nums[i]); res = Math.max(res, sum); } return res; };
- 分治算法:分成三部分,左右中,最大连续自序和要么在左部分,要么在又部分,或者是中间部分。
以本题为例,中部计算就是向中间元素的左侧和右侧分别计算出最大的连续子序列和,相加;左右部分也是相同的递归过程,再比较三者中最大的数
//JS var maxSubArray = function(nums) { //分治法 let getSubArr = (arr, low, high) => { if(low == high) return arr[low]; let lmax = -Number.MAX_SAFE_INTEGER, rmax = -Number.MAX_SAFE_INTEGER, mid = Math.floor((high - low) / 2) + low, sum = 0; let l = getSubArr(nums, low, mid), r = getSubArr(nums, mid + 1, high); for(let i = mid; i >= low; i--){ sum += nums[i]; if(sum > lmax) lmax = sum; } sum = 0; for(let i = mid + 1; i <= high; i++){ sum += nums[i]; if(sum > rmax) rmax = sum; } return Math.max(l, r, lmax + rmax); }; return getSubArr(nums, 0, nums.length - 1); };