背景
自己码了一遍这个算法,然后网上找了下题目分析。发现了更好的解法:解决最大子数组之和的三种方法 ,在此记录一篇简述心得。
题目
求一个整数数组中[和最大]的连续子数组
拿到题目自己理解的有误,一直在想怎么一边计算和一边记录 start&end 索引。其是题目没有这个要求。
解法一,思路如下:
因为最大子数组 startIndex 和 endIndex 不确定,所以肯定需要for循环遍历来确定。 因为遍历所有元素作为 startIndex 需要一层for循环,另外遍历所有元素作为endInex也需要一层for循环。所以一定需要两层for循环。 计算 endIndex 思路:例如数组是[a, b, c, d, e],如果a + b > 当前最新的count,则更新 count;否则不更新,继续累加。 2层遍历结束,最终的 count 就是最大的和。
public static void solution(int[] array) {
int startIndex = 0;
int endIndex = 0;
int count = 0;
//第一层遍历:所有元素作为 startIndex
for (int i = 0; i < array.length; i++) {
// 注意1:一定需要一个 tempCount 来一直做累加,然后把最终值赋给count
int tempCount = 0;
for (int j = i; j < array.length; j++) {
//第二层遍历:计算 i 作为 startIndex 时,哪个元素为和最大的的 endIndex
tempCount += array[j];
if (tempCount > count) {
//注意2:此处有个关键代码[ tempCount > count ],通过此条件更新最终结果
startIndex = i;
endIndex = j;
count = tempCount;
}
}
}
System.out.println("startIndex:" + startIndex + ", endIndex:" + endIndex + ", count:" + count);
}
解法二,思路如下:
先说一下我的思路:数组 [a, b, c, d, e] 中可能有负数,否则最大值就是整个数组元素之和。我们开始遍历累加数组元素,例如 a+b,假如a为正数(如果a为负数则从b开始计算),如果 b 为负数,则判断 b+c 是否为正数,如果b+c是正数则当前最大子序列为 a+b+c,否则再计算 b+c+d是否为正数。以此类推。那么问题来了,虽然感觉这思路方向没问题,但是代码不好写,首先第一层遍历少不了,但是判断 b+c+d 是否为正数是用递归还是用遍历呢?如果用遍历那么效率和解法一相同,用递归代码又变复杂了。怎么办? 答案就在这篇文章:解决最大子数组之和的三种方法 我的思路错误在:遍历是从左到右,但是另一块判断加上右边的b 后对总和是否是正向影响。一左一右,方向不一致,出现了矛盾点。 正确的思路:遍历依然是从左到右,但是累加判断分拆为两点
[条件1]:a+b后和 是否大于 当前的最大值,如果大于当前的最大值则更新最大值 [条件2]:a+b之后的和是否小于b,如果小于b,代表b之前的元素之和是负数,需要抛弃,b为最新的startIndex。 累加的判断拆分为以上两点,但是写代码时要先 判断[条件2],再判断[条件2],因为更新累加和的逻辑一定在代码块的末尾位置。
public static void solution(int[] arr) {
int max=0;
int thisMax=0;
for (int i=0;i<arr.length;i++) {
thisMax+=arr[i];
//若thisMax有没有令arr[i]变小,则从arr[i]重新开始累计
if (thisMax<arr[i]) {
thisMax=arr[i];
}
//若当前的thisMax已经比上一个max大,则将thisMax记为max
if (thisMax>max) {
max=thisMax;
}
}
System.out.println(max);
}
总结:个人觉得以上两种解法就比较清晰,主要记录反思自己思路和正确答案之间的偏差。(文中引用博客使用递归的分治法不好理解,就不再解释)