求最大子序列的和
题目链接:leetcode53 最大子序和
本文主要讲述两种方法:贪心算法和分治法
一、贪心算法
样例数据:int[] a = new int[]{-2,1,-3,4,-1,2,1,-5,4};
简单考虑,先假设最大子序列的和肯定大于等于0。
从左往右遍历数组
- 对于第一个数,a[0] = -2 < 0;假设最大子序列包含a[0],最大子序列的最右边坐标为j,那么最大子序列的和为a[0] + a[1] + … + a[j],因为a[0] < 0;所以a[1] + … + a[j] 肯定大于 a[0] + a[1] + … + a[j];所以a[0]肯定不会出现在最大子序列中。
- 对于第一个数a[1] = 1。当前的最大值为1,先记录下来,此时max = 1,tempMax = 1;
- 对于第二个数a[2] = -3 。a[1] + a[2] = 1 + -3 = -2 < 0;根据1得出的结论,如果最大子序列包含a[1]和a[2]或者仅仅包含a[2]的话,其实是对求最大子序列和是起到负作用的。所以我们这时候把tempMax置为0。
- 对于a[3] = 4, tempMax = 4, max = 4;
- 对于a[4] = -1, tempMax = 3, max = 4;
- 对于a[5] = 2, tempMax = 5, max = 5;
- 对于a[6] = 1, tempMax = 6, max = 6;
- 对于a[7] = -5, tempMax = -1, max = 6;
- 对于a[8] = 4, tempMax = 3, max = 6;
综上所述,我们最最大子序列的时候,可以从左往右遍历,进行累加。如果累加的结果出现负值,这个时候前面的和对于后续求最大子序列和是起到反作用的。最大子序列要么出现在这个累加的过程中,要么就出现在后续的没处理的数组中。
这块比较难以理解,举个例子来说明,对于数组[1,2,3,-3,-6,2,3,5]
对于[0,2]最大值为6,
对于[0,4]和为-3,在计算[5,7]的和中,左边的数据实际上产生的是负的作用。应该被舍弃。
至于出现负值重置为0,是因为对于[1,2,3,-4,1,2]来说
[0,2]的和为6
a[3]虽然小于0,但是a[0]+a[1]+a[2]+a[3] > 0,对于3后面的数值来说,仍然是正的,具有正作用。所以继续累加。
附上代码
public static int maxSubArray(int[] nums) {
if (nums == null || nums.length < 0) {
return 0;
}
int sum = 0;
int max = nums[0]; //这里主要是为了防止整个数列中的所有数都小于0
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
if (max < sum) {
max = sum;
}
if (sum < 0) {
sum = 0;
}
}
return max;
}
二、分治法
分治法的核心思想就是分而治之。
对于一个数组求最大子序列和,无外乎有三种情况
通过二分的方式把数组一分为二,分别为[left, mid]、[mid + 1, right]
- 最大子序列仅出现在左边
- 最大子序列仅出现在右边
- 最大子序列横跨左右两边
对于前两种情况,直接一直递归下去求解就好啦
对于第三种情况。横跨左右两边,说明肯定包含两个元素 [mid] [mid+1]
我们从mid开始,求出包含mid的左边最大的子序列和,
从mid + 1开始,求出mid+1的右边最大的子序列和
然后返回的值就是这三个值中最大的那个。
附上代码
public static int maxSubArray(int[] nums, int left, int right) {
if (left == right) {
return nums[left];
}
int mid = (left + right) / 2;
int leftMaxSum = maxSubArray(nums, left, mid);
int rightMaxSum = maxSubArray(nums, mid + 1, right);
int allMaxSum = 0;
int temp = nums[mid];
int tempMax = temp;
for (int i = mid - 1; i >= left; i--) {
temp += nums[i];
if (tempMax < temp) {
tempMax = temp;
}
}
allMaxSum += tempMax;
temp = nums[mid + 1];
tempMax = temp;
for (int i = mid + 2; i <= right; i++) {
temp += nums[i];
if (tempMax < temp) {
tempMax = temp;
}
}
allMaxSum += tempMax;
return getMax(leftMaxSum, rightMaxSum, allMaxSum);
}
public static int getMax(int a, int b, int c) {
if (a > b) {
return a > c ? a : c;
}
return b > c ? b : c;
}