问题描述:
给定一个整数数组 nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
考虑最大和跟子数组最后一个元素arr[n-1]的关系 。分为3种情况。
(1)最大子数组包括arr[n-1] , 即以arr[n-1] 结尾。
(2)arr[n-1] 单独构成最大子数组。
(3)最大子数组不包含arr[n-1],那么求arr[1..n-1]的最大子数组可以转换为求arr[1...n-2]的最大子数组。
然后动态关系找到。
假设All[i-1]为(arr[0]...arr[i-1])的最大子数组之和 ,End[i-1]为(arr[0]...arr[i-1])中包含arr[i-1]的最大子数组之和。
那么则有End[i-1]=max{End(i-2)+arr[i-1],End(i-1)} All[i-1]=max{End[i-1],All[i-2]}
public static int max(int m,int n){
return m>n?m:n;
}
public int maxSubArray(int[] nums) {
if (nums.length == 0 || nums == null) {
return 0;
}
int n = nums.length;
int[] All = new int[n];
int[] End = new int[n];
All[0] = End[0] = nums[0];
for(int i=1;i<n;i++){
End[i]=max(End[i-1]+nums[i],nums[i]);
All[i]=max(End[i],All[i-1]);
}
return All[n-1];
}
可以观察到每次循环之用到了End[i-1]和All[i-1],所以可以将两个数组用变量代替。
public static int max(int m,int n){
return m>n?m:n;
}
public int maxSubArray(int[] nums) {
if (nums.length == 0 || nums == null) {
return 0;
}
int n = nums.length;
int All = nums[0];
int End = nums[0];
for(int i=1;i<n;i++){
End=max(End+nums[i],nums[i]);
All=max(End,All);
}
return All;
}
算法到这基本解决问题,延伸一下,怎么求子数组的起始位置和终止位置呢?
要做到这点需要两步。
1)用一个变量start保存当前最大子数组的起始位置。
2) 每当最大子数组变化时将当前start和遍历节点i 赋值给保存结果变量。
private static int begin = 0;
private static int end = 0;
public static int maxSubArray(int[] nums) {
if (nums.length == 0 || nums == null) {
return 0;
}
int n = nums.length;
int All = nums[0];
int End = nums[0];
int start = 0;
for(int i=1;i<n;i++){
//End=max(End+nums[i],nums[i]);
if(End+nums[i]<nums[i]){
start = i;
End = nums[i];
}else{
End = End +nums[i];
}
//All=max(End,All);
if(End>All){
All = End;
begin = start;
end = i;
}
}
return All;
}
其实还有方法使用分治法求解,恕在下没想明白,不过多介绍,有兴趣可以自行百度。