分析:该题是一道经典的题,但是很多人理解不了,我在此处进行了总结,大家按我的思路看下去,肯定能理解。
-
使用蛮力法。最外循环为第i个数,第二循环从i开始一直到数组尾j,最内循环则计算i到j子数组的和。
public static int maxSubArray(int arr[]) { int n = arr.length; int ThisSum = 0, MaxSum = 0; for(int i=0; i<n; i++) { //从0开始循环 for(int j=i; j<n; j++) {//从当前位循环到最后一位 ThisSum = 0; for(int k=i; k<j; k++) {//计算i到j的子序列和 ThisSum += arr[k]; } if(ThisSum > MaxSum) { MaxSum = ThisSum; } } } return MaxSum; }
-
该方式重复的计算了i到j之间值,重点在于如何减少重复的计算,在最内的k循环中,可以通过Sum[I, j] = Sum[I, j-1]+arr[j],这样就能避免重复的计算。
public static int maxSubArray(int arr[]) { int size = arr.length; int maxSum = Integer.MAX_VALUE; for(int i=0; i<size; i++) {//同样还是两个循环,要计算的还是i到j序列的和 int sum = 0;//不同的是sum是递增的,即在j++的过程中,前面的sum是不变的 for(int j=i; j<size; j++) { sum += arr[j]; if(sum > maxSum) { maxSum = sum; }//这个判断是获取子序列的关键 } } return maxSum; }
-
注意上面两个方案的叠进顺序,和对第二个解决方案的理解。动态优化依然是减少重复的运算。在上面的算法中,只消除了固定i下j的重复计算,但当i变化时,sum的部分值依然是重复的。那如何分解问题?若已知A[1, j]的最大子数组,那A[1, j+1]的最大子数组等于求解最后一个元素arr[n-1]与最大子数组的关系:①最大子数组包含arr[n-1],即以arr[n-1]结尾;②arr[n-1]单独构成最大子数组;③最大子数组不包含arr[n-1]。三种可能写为All[ i - 1] = max{arr[ i - 1 ], End[ i - 1 ], All[ i - 2 ]},三个选项分为对应①②③。End[i]是包含第i值的最大子数组,All[i]是当前最大子数组。
public static int maxSubArray(int arr[]) { int n = arr.length; int End[] = new int[n];//End[i]表示包含当前值的最大子数组值 int All[] = new int[n];//All[i]表示当前全局最大子数组值 End[0] = All[0] = arr[0];//一开始全为arr[0] for(int i=1; i<n; i++) {//End是个辅助数组,用于比较①② All则在比较③ End[i] = max(End[i-1] + arr[i], arr[i]);//比较当前值是否比包含当前值的序列大 All[i] = max(End[i], All[i-1]);//比较当前值序列是否比不包含当前值序列大 } return All[n-1]; }
-
上面每次只用到End[n-1]和All[n-1],这样只需要定义两个变量即可。
public static int maxSubArray4(int arr[]) { int n = arr.length; int nAll = arr[0]; int nEnd = arr[0]; for(int i=1; i<n; i++) { nEnd = max(nEnd + arr[i], arr[i]);//比较当前值是否比包含当前值的序列大 nAll = max(nEnd, nAll);//比较当前值序列是否比不包含当前值序列大 } return nAll; }