一 题目
在数组中找一个子数组,子数组的每一个元素之和最大并且返回这个数字。
二 滑动窗口实现
可以定义两个指针l和r,分别确定子数组的左右边界。
假设子数组所有元素的和sum,当sum<nums[r]时,说明sum一定不是最优解,
但此时也不能简单粗暴的确定最优值一定是nums[r],
比如这种情况[-6,1,2,2,1],数组总和0小于1,但是此时1也明显不是子数组中元素和最大值。
因此sum<nums[r]只能说明当前子数组一定不是最优解,不能说明nums[i]一定是最优解。
同时当前子数组中的其他子集依旧有可能包含最优解,因此应该将左指针l右滑即可。
var maxSubArray = function(nums){
let l=0;
let r=0;
let max=nums[0];
let sum=nums[0];
while(l<nums.length&&r<nums.length){
if(sum<nums[r]){
max=Math.max(sum,max);
sum-=nums[l++];
}else if(sum>=nums[r]){
max=Math.max(sum,max);
sum+=nums[++r];
}
}
return max;
}
三 动态规划
- 动态规划在查找有很多重叠子问题的情况的最优解时有效。它将问题重新组合成子问题。为了避免多次解决这些子问题,它们的结果都逐渐被计算并被保存,从简单的问题直到整个问题都被解决。因此,动态规划保存递归时的结果,因而不会在解决同样的问题时花费时间。
- 动态规划只能应用于有最优子结构的问题。最优子结构的意思是局部最优解能决定全局最优解(对有些问题这个要求并不能完全满足,故有时需要引入一定的近似)。简单地说,问题能够分解成子问题来解决。
- 实现
以[-2,1,-3,4,-1,2,1,-5,4]为例,用数组中第i项fn[i]记录数组nums[0...i]的最优解。自下而上解决问题。
fn[0]记录nums[0...0]的最优解,即fn[i]=nums[0]=-2。
接着用fn[1]记录nums[0...1]的最优解,
而fn[1]=Math.max(nums[1],nums[1]+fn[0])
即fn[1]=Math.max(1,-1)=1。
接着用fn[2]记录nums[0...2]的最优解,
而fn[2]=Math.max(nums[2],nums[2]+fn[1])
即fn[2]=Math.max(-3,-2)=-2。
以此类推...
var maxSubArray = function(nums){
let fn=[nums[0]];
for(let i=1;i<nums.length;i++){
fn[i]=Math.max(nums[i],fn[i-1]+nums[i]);
}
return Math.max(...fn);
}