最大子数组问题(分治策略实现)

在算法导论4.1最大子数组问题中首先提出的是暴力求解方法即计算所有子数组的组合,然后求其和,寻找最大值。这种方法运行时间为Ω(n^2)。然后提出有没有更好的方法。

使用分治策略的求解方法:

寻找子数组A[low..high]的最大子数组,使用分治技术意味着要将子数组划分为两个规模尽量相等的子数组。也就是说,找到子数组的中央位置(比如mid),然后考虑求解两个子数组A[low..mid]和A[mid..high]。A[low..high]的任何连续子数组A[i..j]所处的位置必然以一下三种情况之一:

  • 完全位于子数组A[low..mid]中,因此low≤i≤j≤mid.
  • 完全位于子数组A[mid+1..high]中,因此mid<i≤j≤high.
  • 跨越了中点,因此low≤i≤mid<j≤high.

因此,A[low..high]的一个最大子数组所处的位置必然是这三个情况之一即A[low..high]的一个最大子数组必然是完全位于A[low..mid]中、完全位于A[mid+1..high]中或者跨越中点的所有子数组和的最大者。递归地求解A[low..mid]和A[mid+1..high]的最大子数组,因为这两个仍是最大子数组问题,只是规模更小。因此,剩下全部工作就是寻找跨越中点的最大子数组,然后在三种情况中选取和最大者。

在线性时间内求出跨越中点的最大子数组。此问题并非原问题规模更小的实例因为加入了限制——求出的子数组必须跨越中点。任何跨越中点的子数组都由两个子数组A[i..mid]和A[mid+1..j]其中low≤i≤mid且mid<j≤high。因此找出形如A[i..mid]和A[mid+1..j]的最大子数组,然后合并即可。

FIND-MAX-CROOSSING-SUBARRAY(A, low, mid, high)伪代码:
 1 //FIND-MAX-CROOSSING-SUBARRAY(A, low, mid, high)
 2 left-sum = -∞
 3 sum = 0
 4 for i = mid downto low
 5     sum=sum+A[i]
 6     if sum>left-sum
 7         left-sum = sum
 8         max-left = i;
 9 right-sum = -∞
10 sum = 0
11 for j = mid+1 to high
12     sum = sum +A[j]
13     if sum > right-sum
14         right-sum = sum
15         max-right = j
16 return(max-left, max-right, left-sum+right-sum)

FIND-MAXIMUM-SUBARRAY(A, low, high)伪代码:

//FIND-MAXIMUM-SUBARRAY(A, low, high)
if high == low
    return(low, high, A[low])  //nase case: only one element
else mid = (low+high)/2
    (left-low, left-right, left-sum) =
        FIND-MAXIMUM-SUBARRAY(A, low, mid)
    (right-low, right-high, right-sum) =
        FIND-MAXIMUM-SUBARRAY(A, mid+1, high)
    (cross-low, cross-high, cross-sum) =
        FIND-MAX-CROSSARRAY(A, low, mid, high)

    if left-sum > right-sum and left-sum > cross-sum
        return (left-low, right-high, right-sum)
    else if right-sum > left-sum and right-sum > cross-sum
        return (right-low, right-high, right-sum)
    else if cross-sum > left-sum and cross-sum > right-sum
        return (cross-low, cross-high, cross-sum)

下面用Java语言实现最大子数组问题(分治策略实现)

FIND-MAX-CROOSSING-SUBARRAY(A, low, mid, high)方法实现
package dividerandconquer;

public class FindMaxCrossingSubAraay {
    private int sum = 0;
    private int sumLeft = 0;
    private int sumRight = 0;
    private int maxLeft;
    private int maxRight;

    public void FindMaxCrossingSubAraay(int a[], int low, int mid, int high) {
        if (low == high) {//子数组个数为1
            sum = a[low];
            maxLeft = low;
            maxRight = low;
        }else {//子数组元素个数至少两个
            sumLeft = a[mid];
            sumRight = a[mid+1];
            maxLeft = mid;
            maxRight = mid+1;
            for (int i = mid; i >= 0; i--) {
                sum+=a[i];
                if (sum > sumLeft) {
                    sumLeft = sum;
                    maxLeft = i;
                }
            }

            sum = 0;
            for (int i = mid+1; i <= high; i++) {
                sum += a[i];
                if (sum > sumRight) {
                    sumRight = sum;
                    maxRight = i;
                }
            }
            sum = sumLeft + sumRight;
        }
    }

    public int getSum() {
        return sum;
    }

    public int getSumLeft() {
        return sumLeft;
    }

    public int getSumRight() {
        return sumRight;
    }

    public int getMaxLeft() {
        return maxLeft;
    }

    public int getMaxRight() {
        return maxRight;
    }

}

FIND-MAXIMUM-SUBARRAY(A, low, high)方法实现

package dividerandconquer;

public class FindMaximumSubArray {

    private int low;
    private int high;
    private int sum;

    public void FindMaximumSubArray(int a[], int low, int high) {
        int mid; //中间位置
        //左数组
        int leftLow;
        int leftHigh;
        int leftSum;
        //右数组
        int rightLow;
        int rightHigh;
        int rightSum;
        //中间数组
        int crossLow;
        int crossHigh;
        int crossSum;

        if (high == low) {//只有一个元素
            this.high = low;
            this.low = low;
            this.sum = a[low];
        }else {//至少两个元素
            mid =(int) ((low + high)/2);//向下取整

            FindMaximumSubArray calLeft = new FindMaximumSubArray();//搜索左边
            calLeft.FindMaximumSubArray(a, low, mid);
            leftLow = calLeft.getLow();
            System.out.println("FindMaximumSubArray.leftLow="+leftLow);
            leftHigh = calLeft.getHigh();
            System.out.println("FindMaximumSubArray.leftHigh="+leftHigh);
            leftSum = calLeft.getSum();
            System.out.println("FindMaximumSubArray.leftSum="+leftSum);

            FindMaximumSubArray calRight = new FindMaximumSubArray();//搜索右边
            calRight.FindMaximumSubArray(a, mid+1, high);
            rightLow = calRight.getLow();
            System.out.println("FindMaximumSubArray.rightLow="+rightLow);
            rightHigh = calRight.getHigh();
            System.out.println("FindMaximumSubArray.rightHigh="+rightHigh);
            rightSum = calRight.getSum();
            System.out.println("FindMaximumSubArray.rightSum="+rightSum);

            FindMaxCrossingSubAraay calCross = new FindMaxCrossingSubAraay();//搜索从中间开始
            calCross.FindMaxCrossingSubAraay(a, low, mid, high);
            crossLow = calCross.getMaxLeft();
            System.out.println("FindMaxCrossingSubArray.crossLow="+crossLow);
            crossHigh = calCross.getMaxRight();
            System.out.println("FindMaxCrossingSubArray.crossHigh="+crossHigh);
            crossSum = calCross.getSum();
            System.out.println("FindMaxCrossingSubArray.crossSum="+crossSum);

            if ((leftSum >= rightSum) && (leftSum > crossSum)) {
                this.low = leftLow;
                this.high = leftHigh;
                this.sum = leftSum;
            }else if ((rightSum >= leftSum)&&(rightSum >= crossSum)) {
                this.low = rightLow;
                this.high = rightHigh;
                this.sum = rightSum;
            }else {
                this.low = crossLow;
                this.high = crossHigh;
                this.sum = crossSum;
            }

        }

    }

    public int getLow() {
        return low;
    }

    public int getHigh() {
        return high;
    }

    public int getSum() {
        return sum;
    }

}

FIND-MAXIMUM-SUBARRAY(A, low, high)测试程序:

package dividerandconquer;

import java.util.Random;

public class testFindMaximumSubArray {
    int length;//getter & setter
    int a[] = new int[length];//setter
    int low;
    int high;
    int mid;
    int sum;

    Random random =new Random();

    private void initTest1(){
        low = 0;
        high =length-1;
        sum = 0;
    }

    private void cal(){
        FindMaximumSubArray cal = new FindMaximumSubArray();
        cal.FindMaximumSubArray(a, low, high);
        sum = cal.getSum();
        low = cal.getLow();
        high = cal.getHigh();
    }

    private void calPrint() {
        // 输出数组
        System.out.print("a=[");
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i]+" ");
        }
        System.out.println("]");
        //输出最大子数组
        System.out.print("a["+(low+1)+"..."+(high+1)+"]=[");
        for (int i = low; i <= high; i++) {
            System.out.print(a[i]+" ");
        }
        System.out.println("]");
        System.out.println("sum="+sum);
    }

    public void test(){
        initTest1();
        cal();
        calPrint();
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public void setA(int[] a) {
        this.a = a;
    }

}

主入口程序:

package dividerandconquer;

import java.util.Random;

public class testFindMaximumSubArray {
    int length;//getter & setter
    int a[] = new int[length];//setter
    int low;
    int high;
    int mid;
    int sum;

    Random random =new Random();

    private void initTest1(){
        low = 0;
        high =length-1;
        sum = 0;
    }

    private void cal(){
        FindMaximumSubArray cal = new FindMaximumSubArray();
        cal.FindMaximumSubArray(a, low, high);
        sum = cal.getSum();
        low = cal.getLow();
        high = cal.getHigh();
    }

    private void calPrint() {
        // 输出数组
        System.out.print("a=[");
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i]+" ");
        }
        System.out.println("]");
        //输出最大子数组
        System.out.print("a["+(low+1)+"..."+(high+1)+"]=[");
        for (int i = low; i <= high; i++) {
            System.out.print(a[i]+" ");
        }
        System.out.println("]");
        System.out.println("sum="+sum);
    }

    public void test(){
        initTest1();
        cal();
        calPrint();
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public void setA(int[] a) {
        this.a = a;
    }

}

运行结果:

a=[1 -2 3 10 -4 7 2 -5 ]
a[3...7]=[3 10 -4 7 2 ]
sum=18
时间: 2024-10-01 02:40:01

最大子数组问题(分治策略实现)的相关文章

【算法导论】最大子数组

1.描述:找出数组A的和最大的非空连续子数组,我们称这样的连续子数组为最大子数组. 2. 用分治策略来求解. a. 假设我们要求A的子数组A[low, high]的最大子数组.根据分治策略,我们先将A[low,high] 平分 b. 那么 A[low,highj]的子数组A[i,j]只有三种可能 a)完全位于A[low, mid]; 此时 low <= i <= j <= mid b)  完全位于A[nid+1, high]中,此时 mid + 1 <= i <= j <

分治策略 &nbsp; 最大子数组问题

递归式 递归式与分治方法是紧密相关的,因为使用递归式可以很自然地刻画分治算法的运行时间.一个递归式就是一个等式或不等式,它通过更小的输入上的函数值来描述一个函数.例如,在2.3.2节,我们用递归式描述了MERGE-SORT过程的最坏情况运行时间T(n): Θ(1)        若n=1 T(n) =                         (4.1) 2T(n/2)+Θ(n)    若n>1 求解可得T(n)=Θ(nlgn) 递归式可以有很多形式.例如,一个递归算法可能将问题划分为规模

最大子数组问题全解

问题描述 给定一个整数数组,找到一个具有最大和的子数组,返回其最大和. 问题解析 很经典的一个问题,下面给出3种解法,暴力解法.分治算法.动态规划.这个题Leetcode上有大量测试数据,只不过最后两个测试数据要求算法复杂度为n,只能用动态规划来解,可以借鉴一下,链接见这里https://leetcode.com/problems/maximum-subarray/description/ 问题解决 1.暴力解法 穷举所有的子串,计算他们的和,然后从中找出最大的一个. //最大子数组的暴力解法

第四章 分治策略 4.1 最大子数组问题 (暴力求解算法)

/** * 最大子数组的暴力求解算法,复杂度为o(n2) * @param n * @return */ static MaxSubarray findMaxSubarraySlower(int[] n) { long tempSum = 0; int left = 0; int right = 0; long sum = Long.MIN_VALUE; for (int i = 0; i < n.length; i++) { for (int j = i; j < n.length; j++

第四章 分治策略 4.1 最大子数组问题 (减治法,别人的,拿来看看)

/** * 获得连续子数组的最大和 * * @author dfeng * */ private static long getMax(long a, long b) { return a > b ? a : b; } /** * 获得连续子数组的最大和 * * @param array * @return 最大和,此处用了Long型是为了表示当参数为null或空时,可以返回null,返回其它任何数字都可能引起歧义. */ public static Long getMax(int[] arra

第四章 分治策略——最大子数组问题

最大子数组问题 方法一:暴力求解方法 我们可以很容易地设计出一个暴力方法来求解本问题:简单地尝试没对可能的子数组,共有O(n2)种 #include<iostream> using namespace std; #define INT_MIN 0x80000000 int main() { int arr[10]={9,8,-3,-5,7,-39,79,-37,8,9}; int i,j; int sum=0,maxsum=INT_MIN; int imax; for(i=0;i<10;

第四章 分治策略 4.1 最大子数组问题(自己想的,不知道是不是减治法)

package chap04_Divide_And_Conquer; import static org.junit.Assert.*; import java.util.Arrays; import org.junit.Test; /** * 算反导论第四章 4.1 最大子数组 * * @author xiaojintao * */ public class Maximum_Subarray_Problem { /** * 最大子数组类 left为头部索引,right为尾部索引,sum为数组和

分治策略---求最大子数组

只有当数组中包含负数时,最大子数组问题才有意义.如果所有元素都是非负的,最大子数组问题没有任何意义,因为整个数组和肯定是最大的 1 public class FindMaxSubArrayDemo { 2 public static void main(String[] args) { 3 int[] arr = {13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7}; 4 int[] result_arr = fi

分治策略之最大子数组问题

从股票买卖问题转换成为一个求最大子数组问题 天 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 价格 100 113 110 85 105 102 86 63 81 101 94 106 101 79 94 90 97 变化 13 -3 -25 20 -3 -16 -23 18 20 -7 12 -5 -22 15 -4 7 暴力求解所花费的时间是O(n^2) 采用分治技术来求解最大子数组问题,假定要求的数组为A[low..high]的最大子数组. 将子数组划分