今天在LeetCode上遇到一个求最大连续子序列和的问题,如下:
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example:
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
先上正确答案,用动态规划处理这个问题。
首先介绍一下什么是动态规划,简单来说就是:
把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解。
分析一下这个问题:
设数组为a,第一个值为a[0],第二个值为a[1],以此类推,第n个值为a[n]。
设以a[i]为子序列最后一项时,连续最大子序和为F(i)。
注意:这里只指定a[i]作为最后一项,并不指定开始项,也就是说这里的F(i)是以a[i]前任意项开始,a[i]项结尾的连续整数和。
- 当该数组中只有一个数值a[0]时,F(0) = a[0]。
- 当该数组中有多个数值时,F(i) = max { a[i], a[i] + F(i-1) }。
写两个值看看这个方程:
F(0) = a[0]
F(1) = max{ a[1], a[1] + F(0) } = max{ a[1], a[0] + a[1] }
F(2) = max{ a[2], a[2] + F(1) } = max{ a[2], a[0] + a[1] + a[2] } 或= max{ a[2], a[1] + a[2] }
以此类推。
接下来写代码:
/**
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function(nums) {
let max = nums[0];
let len = nums.length;
if (len === 1) {
return max;
} else {
let sum = 0;
for (let i = 0; i < len; i ++) {
sum += nums[i];
if (sum < nums[i]) {
sum = nums[i];
}
if (max < sum) {
max = sum;
}
}
}
return max;
};
只要思路对了,写代码是再容易不过的事情啦!
补充分而治之法(递归)求解,以下为js代码:
/**
* @param {number[]} nums
* @param {number} left 左端开始位置
* @param {number} right 右端结束位置
* @return {number}
*/
maxSubArray (nums, left, right) {
if (left === right) {
return nums[0]
}
let mid = Math.floor((left + right) / 2)
// 求左侧最大值
let leftMax = this.maxSubArray(nums, left, mid)
// 求右侧最大值
let rightMax = this.maxSubArray(nums, mid + 1, right)
let sum = 0
let lmax = nums[mid]
let rmax = nums[mid + 1]
// 从中线往左遍历,取最大值
for (let i = mid; i >= left; i--) {
sum += nums[i]
if (sum > lmax) {
lmax = sum
}
}
sum = 0
// 从中线往右遍历,取最大值
for (let i = mid + 1; i <= right; i++) {
sum += nums[i]
if (sum > rmax) {
rmax = sum
}
}
let midMax = lmax + rmax
return leftMax > rightMax ? leftMax > midMax ? leftMax : midMax : rightMax > midMax ? rightMax : midMax
}
// 调用
maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4], 1, 9)
=============================================
补充记录自己最开始的复杂写法:
/**
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function(nums) {
var arr = [];
for (let i = 0; i < nums.length; i ++) {
let sum = nums[i];
for (let j = i + 1; j < nums.length; j ++) {
sum += nums[j];
arr.push(sum);
}
}
arr.sort(function(a,b){
return a-b;
});
return arr[arr.length - 1];
};
我去,这是什么垃圾?把所有值都丢到一个新数组中这种智障代码真的是我写的?这样数组一大的话,arr数组将耗费巨大的内存,果不其然,提交后显示堆内存不足。
好吧,优化一下代码,如下:
/**
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function(nums) {
var max = nums[0];
var len = nums.length;
for (let i = 0; i < len; i ++) {
let sum = nums[i];
for (let j = i + 1; j < len; j ++) {
max = max > sum ? max : sum;
sum += nums[j];
}
max = max > sum ? max : sum;
}
return max;
};
这里取消了新数组,但是此时的算法时间复杂度是O(n^2),虽然可以求出结果但是耗时过长。