首页 文章

使用D&C /递归的最大子数组

提问于
浏览
0

我想用一个展示(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

    1.Kadane算法的实现是错误的,它会在带有一些负数的数组上失败 . 正确的应该是这样的:

    public static void Kadane(int array[]) {
            int max_ending_here = 0;
            for (int i = 0; i < array.length; i++) {
                max_ending_here = Math.max(array[i], 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);
        }
    

    您的代码中存在许多错误,例如:

    2.在计算答案之前,应将 maxEndingHere 初始化为0:

    [lowIndex,mid)
    [mid, highIndex]
    [lowCenter, highCenter]
    

    现在它只在第一次迭代之前被初始化 .

    1. lowCenter 应初始化为 lowIndex

    这个程序不必要地冗长而复杂......我不知道我是否错过了更多错误......

  • 1

    解决方案非常简单, pv 是一个变量,它向我们展示了从k开始的子数组的总和,其中0 <= k <= n-1到我们在第i次迭代中访问的变量 . 数组s让我们跟踪最大子数组,s [i]也是在第i次迭代中找到的最大子数组 . c是原始数组 . p是指向在第i次迭代中找到的最大子数组的开始的指针 . tp是将p更新为正确值的临时指针

    /**
     * @author : Yash M. Sawant
     */
    
    
    
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    #define MAXLENGTH 17
    #define MINVALUE -999
    
    
    
    int main() {
        int i;
    
        int s[MAXLENGTH]; s[0] = 0;
        int c[MAXLENGTH] = {0, 13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7};
        int pv = 0, p = -1, tp = -1;
        for(i = 1 ; i < MAXLENGTH ; i ++) {
            printf("%4d ", c[i]);
            if(s[i - 1] < pv + c[i]) {
                s[i] = pv + c[i];
                pv = pv + c[i];
                if(tp > p) {
                    p = tp;
                }
            } else {
                s[i] = s[i - 1];
                pv = pv + c[i];
                if(pv < 0) {
                    pv = 0; tp = i;
                }
            }
        }
        printf("\n");
        for(i = 0 ; i < MAXLENGTH ; i ++) {
            printf("%4d ", s[i]);
        }
        printf("\n");
        printf("Max Sub Array = %d and Starts at %d ", s[MAXLENGTH - 1], p);
        return 0;
    
    }
    
  • 1

    找到具有最大总和(D / C递归方式)的子阵列的更简洁方法:

    // A Divide and Conquer based Java solution
    // To find a subarray with the maximum sum
    
    import java.util.Arrays;
    import java.util.Scanner;
    
    class MaxSubArray {
    
        private static Result maxCrossingSum(int arr[], int l, int m, int h) {
    
            int sum = 0;
            int maxLeft = 0;
            int leftSum = Integer.MIN_VALUE;
            for (int i = m; i >= l; i--) {
                sum = sum + arr[i];
                if (sum > leftSum) {
                    leftSum = sum;
                    maxLeft = i;
                }
            }
    
            sum = 0;
            int rightSum = Integer.MIN_VALUE;
            int maxRight = arr.length - 1;
            for (int i = m + 1; i <= h; i++) {
                sum = sum + arr[i];
                if (sum > rightSum) {
                    rightSum = sum;
                    maxRight = i;
                }
            }
    
            return new Result(maxLeft, maxRight, leftSum + rightSum);
        }
    
        private static Result maxSubArray(int[] A, int low, int high) {
    
            if (low == high) {
                return new Result(low, high, A[low]);
            }
    
            int mid = (low + high) / 2;
    
            Result leftSubArray = maxSubArray(A, low, mid);
            Result rightSubArray = maxSubArray(A, mid + 1, high);
            Result maxCrossingSubArray = maxCrossingSum(A, low, mid, high);
    
            int leftSum = leftSubArray.sum;
            int rightSum = rightSubArray.sum;
            int crossSum = maxCrossingSubArray.sum;
    
            if (leftSum > rightSum && leftSum > crossSum) {
                return new Result(leftSubArray.low, leftSubArray.high, leftSubArray.sum);
            } else if (rightSum > leftSum && rightSum > crossSum) {
                return new Result(rightSubArray.low, rightSubArray.high, rightSubArray.sum);
            } else {
                return new Result(maxCrossingSubArray.low, maxCrossingSubArray.high,
                    maxCrossingSubArray.sum);
            }
        }
    
        public static class Result {
    
            public int low;
            public int high;
            public int sum;
            public Result(int low, int high, int sum) {
                this.low = low;
                this.high = high;
                this.sum = sum;
            }
    
        }
    
        /* Driver program to test maxSubArray */
        public static Result maxSubArray(int[] arr) {
            return maxSubArray(arr, 0, arr.length - 1);
        }
    
        public static void main(String[] args) {
    
            Scanner sc = new Scanner(System.in);
            String[] arrString = sc.nextLine().split(" ");
    
            int n = arrString.length;
    
            int[] arr = new int[n];
            for (int i = 0; i < n; i++) {
                arr[i] = Integer.parseInt(arrString[i]);
            }
    
            Result result = maxSubArray(arr);
    
            int[] subArray = new int[result.high - result.low + 1];
            int j = 0;
            for (int i = result.low; i <= result.high; i++) {
                subArray[j++] = arr[i];
            }
    
            System.out.println(Arrays.toString(subArray));
            System.out.println("Sum : " + result.sum);
        }
    }
    

相关问题