题目
给定一个序列(至少含有 1 个数),从该序列中寻找一个连续的子序列,使得子序列的和最大。
例如,给定序列 [-2,1,-3,4,-1,2,1,-5,4]
,
连续子序列 [4,-1,2,1]
的和最大,为 6
。
扩展练习:
若你已实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
分析
一开始用的最简单最直接的方法,挨个的把每个子序列的和求一遍,时间复杂度O(n²),提交之后超时了。。。。。
嗯再换个方法,看了结题报告之后。。
第二种方法呢,动态规划。遍历一遍序列,从后向前遍历,每次加进去一个新的数字nums[i],比较nums[i]和nums[i]+上次循环得到的sum的和,谁比较大,令sum等于较大的数字,然后令maxSum = 较大的数字(maxSum同sum比较谁比较大)。时间复杂度O(n)。
第三种方法,分治算法,时间复杂度O(nlogn),递归找出左半部分的最大子序列和,递归找出右半部分最大子序列和。
代码:
//方法一
public static int maxSubArray1(int[] nums) {//超时
int maxSum = nums[0];
for (int i = nums.length - 1; i >= 0 ; i--) {
int temp = 0;
for (int j = i; j < nums.length; j++) {
temp += nums[j];
if (temp > maxSum)
maxSum = temp;
}
}
return maxSum;
}
//方法二
public static int maxSubArray2(int[] nums){
int sum = nums[nums.length-1];
int maxSum = sum;
for (int i = nums.length - 2; i >= 0; i--) {
sum = Math.max(nums[i] , nums[i] + sum);
maxSum = Math.max(sum,maxSum);
}
return maxSum;
}
//方法三
public static int maxSubArray(int[] nums){
return searchMax(nums,0,nums.length-1);
}
public static int searchMax(int[] nums, int left, int right){
if (left == right) return nums[left];
int mid = (left + right)/2;
int l = searchMax(nums, left, mid);
int r = searchMax(nums, mid+1, right);
int numsMid = nums[mid];
for (int i = mid - 1, temp = numsMid; i >= left ; i--) {
temp += nums[i];
if (temp > numsMid) numsMid = temp;
}
for (int i = mid +1, temp = numsMid; i <= right; i++) {
temp += nums[i];
if (temp > numsMid) numsMid = temp;
}
if ( l >= r && l >= numsMid) return l;
if ( r >= l && r >= numsMid) return r;
return numsMid;
}