我想用一个展示(n log n)的算法实现最大子数组问题:
找到最大的连续子数组,或数组中连续元素的最大总和 .
假设:并非所有元素都是负数
我有点工作的解决方案;问题在于重叠的中心数组,以及指定重叠子问题的适当索引,一些数组我得到正确答案而不是其他问题 .
仅仅为了比较和检验正确性我实现了一个称为Kadane算法的解决方案(我相信复杂性是Omega(n)) .
这是Kandane的算法(http://en.wikipedia.org/wiki/Maximum_subarray_problem):
public static void Kadane(int array[]) {
int max_ending_here = 0;
for (int i = 0; i < array.length; i++) {
max_ending_here = max_ending_here + array[i];
max_so_far = Math.max(max_so_far, max_ending_here);
}
System.out.println("Kadane(int array []): " + max_so_far);
}
我的递归实现,使用divide&conquer来比较子数组的max,然后对具有max的子数组进行递归调用,直到递归结束
public static void findMaxSubArray(int[] array, int lowIndex, int highIndex) {
int mid = 0;
int arrayLength = 0;
int maxEndingHere = 0;
if (array == null) {
throw new NullPointerException();
} else if
//base condition
(array.length < 2 || (highIndex==lowIndex)) {
maxLowIndex = lowIndex;
maxHighIndex = highIndex;
System.out.println("findMaxSubArray(int[] array, int lowIndex, int highIndex)");
System.out.println("global Max Range, low:" + maxLowIndex + " high: " + maxHighIndex);
System.out.println("global Max Sum:" + globalMaximum);
} else {
System.out.println();
int lowMidMax = 0;
int midHighMax = 0;
int centerMax = 0;
//array length is always the highest index +1
arrayLength = highIndex + 1;
//if even number of elements in array
if (array.length % 2 == 0) {
mid = arrayLength / 2;
System.out.println("low: " + lowIndex + " mid: " + mid);
for (int i = lowIndex; i < mid; i++) {
System.out.print(array[i] + ",");
}
//calculate maximum contigous array encountered so far in low to mid indexes
for (int i = lowIndex; i < mid; i++) {
maxEndingHere = maxEndingHere + array[i];
if (maxEndingHere < 0) {
maxEndingHere = 0;
}
if (globalMaximum < maxEndingHere) {
//new maximum found
globalMaximum = lowMidMax = maxEndingHere;
lowIndex = i;
}
}
//end low mid calc
for (int i = mid; i <= highIndex; i++) {
System.out.print(array[i] + ",");
}
System.out.println("mid: " + mid + " high: " + highIndex);
//calculate maximum contigous array encountered so far in mid to high indexes
for (int i = mid; i <= highIndex; i++) {
maxEndingHere = maxEndingHere + array[i];
if (maxEndingHere < 0) {
maxEndingHere = 0;
}
if (globalMaximum < maxEndingHere) {
//new maximum found
globalMaximum = midHighMax = maxEndingHere;
mid = i;
}
}
//end mid high calc
//calculate maximum contigous array encountered so far in center array
int lowCenter = mid -1;
int highCenter = highIndex -1;
System.out.println("lowCenter: " + lowCenter + " highCenter: " + highCenter);
for (int i = lowCenter; i < highCenter; i++) {
System.out.print(array[i] + ",");
}
//calculate maximum contigous array encountered so far in low to mid indexes
for (int i = lowCenter; i < highCenter; i++) {
maxEndingHere = maxEndingHere + array[i];
if (maxEndingHere < 0) {
maxEndingHere = 0;
}
if (globalMaximum < maxEndingHere) {
//new max found
globalMaximum = centerMax = maxEndingHere;
lowCenter = i;
}
}
//end center calc
//determine which range contains the maximum sub array
//if lowMidMax <= midHighMax and centerMax
if (lowMidMax >= midHighMax && lowMidMax >= centerMax) {
maxLowIndex = lowIndex;
maxHighIndex = mid;
//recursive call
findMaxSubArray(array, lowIndex, mid);
}
if (midHighMax >= lowMidMax && midHighMax >= centerMax) {
maxLowIndex = mid;
maxHighIndex = highIndex;
//recursive call
findMaxSubArray(array, mid, highIndex);
}
if (centerMax >= midHighMax && centerMax >= lowMidMax) {
maxLowIndex = lowCenter;
maxHighIndex = highCenter;
//recursive call
findMaxSubArray(array, lowCenter, highCenter);
}
}//end if even parent array
//else if uneven array
else {
mid = (int) Math.floor(arrayLength / 2);
System.out.println("low: " + lowIndex + " mid: " + mid);
for (int i = lowIndex; i < mid; i++) {
System.out.print(array[i] + ",");
}
//calculate maximum contigous array encountered so far in low to mid indexes
for (int i = lowIndex; i < mid; i++) {
maxEndingHere = maxEndingHere + array[i];
if (maxEndingHere < 0) {
maxEndingHere = 0;
}
if (globalMaximum < maxEndingHere) {
//new maximum found
globalMaximum = lowMidMax = maxEndingHere;
lowIndex = i;
}
}
//end low mid calc
System.out.println("mid+1: " + (mid + 1) + " high: " + highIndex);
for (int i = mid + 1; i <= highIndex; i++) {
System.out.print(array[i] + ",");
}
//calculate maximum contigous array encountered so far in mid to high indexes
for (int i = mid + 1; i <= highIndex; i++) {
maxEndingHere = maxEndingHere + array[i];
if (maxEndingHere < 0) {
maxEndingHere = 0;
}
if (globalMaximum < maxEndingHere) {
//new maximum found
globalMaximum = midHighMax = maxEndingHere;
mid = i;
}
}
//end mid high calc
//calculate maximum contigous array encountered so far in center array
int lowCenter = mid;
int highCenter = highIndex -1;
System.out.println("lowCenter: " + lowCenter + " highCenter: " + highCenter);
for (int i = lowCenter; i < highCenter; i++) {
System.out.print(array[i] + ",");
}
//calculate maximum contigous array encountered so far in low to mid indexes
for (int i = lowCenter; i < highCenter; i++) {
maxEndingHere = maxEndingHere + array[i];
if (maxEndingHere < 0) {
maxEndingHere = 0;
}
if (globalMaximum < maxEndingHere) {
//new max
globalMaximum = centerMax = maxEndingHere;
lowCenter = i;
}
}
//end center calc
//determine which range contains the maximum sub array
//if lowMidMax <= midHighMax and centerMax
if (lowMidMax >= midHighMax && lowMidMax >= centerMax) {
maxLowIndex = lowIndex;
maxHighIndex = mid;
//recursive call
findMaxSubArray(array, lowIndex, mid);
}
if (midHighMax >= lowMidMax && midHighMax >= centerMax) {
maxLowIndex = mid;
maxHighIndex = highIndex;
//recursive call
findMaxSubArray(array, mid, highIndex);
}
if (centerMax >= midHighMax && centerMax >= lowMidMax) {
maxLowIndex = lowCenter;
maxHighIndex = highCenter;
//recursive call
findMaxSubArray(array, lowCenter, highCenter);
}
}//end odd parent array length
}//end outer else array is ok to computed
}//end method
结果:使用数组subArrayProblem1 = {1,2,3,4,5,6,7,8};
Kadane(int array []): 36 低:0中:4 1,2,3,4,5,6,7,8,中:4高:7低中:6高中:6
findMaxSubArray(int [] array,int lowIndex,int highIndex)global Max Range,low:7 high:7 global Max Sum:36 BUILD SUCCESSFUL(总时间:0秒)
问题虽然与Kadane相比,全局最大总和是正确的,但低指数和高指数范围反映了最后一次重复调用 .
结果:使用数组subArrayProblem = {100,113,110,85,105,102,86,63,81,101,94,106,101,79,94,90,97};
Kadane(int array []):1607 low:0 mid:8 100,113,110,85,105,102,86,63,mid 1:9 high:16 101,94,106,101,79,94,90,97,lowCenter:16 highCenter:15
findMaxSubArray(int [] array,int lowIndex,int highIndex)global Max Range,low:16 high:16 global Max Sum:1526
全局最大值不正确,请注意差异实际上是1个元素,即元素81
3 回答
1.Kadane算法的实现是错误的,它会在带有一些负数的数组上失败 . 正确的应该是这样的:
您的代码中存在许多错误,例如:
2.在计算答案之前,应将
maxEndingHere
初始化为0:现在它只在第一次迭代之前被初始化 .
lowCenter
应初始化为lowIndex
这个程序不必要地冗长而复杂......我不知道我是否错过了更多错误......
解决方案非常简单,
pv
是一个变量,它向我们展示了从k开始的子数组的总和,其中0 <= k <= n-1到我们在第i次迭代中访问的变量 . 数组s让我们跟踪最大子数组,s [i]也是在第i次迭代中找到的最大子数组 . c是原始数组 . p是指向在第i次迭代中找到的最大子数组的开始的指针 . tp是将p更新为正确值的临时指针找到具有最大总和(D / C递归方式)的子阵列的更简洁方法: