最大子数组问题(分治法,暴力法),最后附有分治法JAVA实现

问题描述:给定一个数组A,寻找A的和最大的非空连续子数组,我们称这样的连续子数组为最大子数组


  • 第一种方法:遍历,从第一个元素一直遍历到最后一个元素,算出和最大的子数组,算法复杂度为Θ( n 2 n^2 n2),容易实现,但不建议使用;

  • 第二种方法:分治策略
    接下来主要介绍分治策略的实现:

  • 使用分治策略意味着我们要将子数组划分为两个规模尽量相等的子数组。因此首先要找到子数组的中央位置,比如mid,然后考虑求解两个子数组A[low…mid]和A[mid+1…high]。

  • A[high…low]的任何连续子数组A[i…j]所处的位置必然是以下三种情况之一:

  • 1、完全位于A[low…mid]

  • 2、完全位于A[mid+1…high]

  • 3、跨越了中点,因此 l o w ≤ low\leq low i ≤ i\leq i m i d ≤ mid\leq mid j ≤ j\leq j h i g h high high

  • 实际上,A[low…high]的一个最大子数组必然是这三种情况之一,对于前两种情况,我们可以递归地求解,因为这两个问题仍然是最大子数组问题,只是规模更小。因此剩下的全部工作就是寻找跨越中点的最大子数组,然后在三种情况下选取和最大者。

  • 寻找跨越中点的最大子数组并非是原问题的一个实例,因为加入了限制,必须跨越中点。

  • 因为任何跨越中点的子数组都由A[i…mid]和A[mid…j]构成。因此,我们只需要找到形如A[i…mid]和A[mid+1…j]的最大子数组,然后将其合并即可。

FIND-MAX-CROSSING-SUBARRAY(A,low,mid,high)
    left-sum = -∞
    sum = 0
    for i = mid downto low
        sum = sum + A[i]
        if sum > left-sum
            left-sum = sum
            max-left = i
    right-sum = -∞
    sum = 0
    for j = mid + 1 to high
        sum = sum + A[j]
        if sum > right-sum
            right-sum = sum
            max-right = j
    return (max-left,max-right,left-sum + right-sum)

总循环迭代数为n,线性时间复杂度

  • 求解最大子数组问题的分治算法的伪代码:
FIND-MAXIMUN-SUBARRAY(A,low,high)
    if high == low
        return (low,high,A[low])   //base case; only one element
    else mid = [(low + high)/2]
        (left-low,left-high,left-sum) = FIND-MAXIMUN-SUBARRAY(A,low,mid)
        (right-low,right-low,right-sum) = FIND-MAXIMUN-SUBARRAY(A,mid + 1,high)
        (cross-low,cross-high,cross-sum) = FIND-MAXIMUN-SUBARRAY(A,low,mid,high) 
        if left-sum >= right-sum and left-sum >= cross-sum
            return (left-row,left-high,left-sum)
        if right-sum >= left-sum and right-sum >= cross-sum
            return (right-low,right-high,right-sum)
        else
            return (cross-low,cross-high,cross-sum)

算法复杂度:Θ( n lg ⁡ n\lg nlg n n n)

  • 非递归
FIND(A,low.high)
    all-sum = A[low]
    all-low = low
    all-right = low
    for i = low to high - 1
        for j = i +1 down to low
            sum = sum + A[j]
            if sum > all-sum
                all-sum = sum
                all-low = j
                all-high = i +1
    return (all-low,all-high,all-sum)

这里我们用JAVA实现一下(分治法):

/**
 * FileName: FIND_MAXIMUM_SUBARRAY
 * Author:   Jerry
 * Date:     2020/3/9 22:22
 * Description:
 */

public class FIND_MAXIMUM_SUBARRAY {
    int []A;
    int low;
    int high;
    int max_sum;

    FIND_MAXIMUM_SUBARRAY(int[] A, int low, int high){
        this.A=A;
        this.low=low;
        this.high=high;
        if(high==low){
            max_sum=A[low];
            return;
        }

        A();

    }
    public void A(){
        int mid = (low+high)/2;
        FIND_MAXIMUM_SUBARRAY fms_left = new FIND_MAXIMUM_SUBARRAY(A,low,mid);
        int left_low  = fms_left.low();
        int left_high = fms_left.high();
        int left_sum = fms_left.max_sum();

        FIND_MAXIMUM_SUBARRAY fms_right = new FIND_MAXIMUM_SUBARRAY(A,mid+1,high);
        int right_low = fms_right.low();
        int right_high = fms_right.high();
        int right_sum = fms_right.max_sum();

        FIND_MAX_CROSSING_SUBARRAY fmcs_mid = new FIND_MAX_CROSSING_SUBARRAY(A,low,mid,high);
        int cross_low = fmcs_mid.max_left();
        int cross_high = fmcs_mid.max_right();
        int cross_sum = fmcs_mid.max_sum();



        if(left_sum>=right_sum&&left_sum>=cross_sum){
            low = left_low;
            high = left_high;
            max_sum = left_sum;
        }
        else if(right_sum>=left_sum&&right_sum>=cross_sum){
            low = right_low;
            high=right_high;
            max_sum= right_sum;
        }
        else{
            low = cross_low;
            high = cross_high;
            max_sum= cross_sum;
        }
    }

    public int low() {
        return low;
    }

    public int high(){
        return high;
    }

    public int max_sum(){
        return max_sum;
    }

    public static void main(String[]args){
        int []A = {0,13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5
        ,-22,15,-4,7};
        FIND_MAXIMUM_SUBARRAY FMS = new FIND_MAXIMUM_SUBARRAY(A,0,A.length-1);
        System.out.println("LOW:"+FMS.low()+" High:"+FMS.high()+" SUM:"+FMS.max_sum());
    }

}
class FIND_MAX_CROSSING_SUBARRAY{
    int[] A;
    int low,mid,high,max_left,max_right,left_sum,right_sum;
    FIND_MAX_CROSSING_SUBARRAY(int[] A, int low, int mid, int high){
        this.A=A;
        this.low=low;
        this.mid = mid;
        this.high=high;

        left_sum =Integer.MIN_VALUE;
        int sum=0;
        max_left=0;
        max_right=0;
        for(int i= mid;i>=low;i--){
            sum+=A[i];
            if (sum>left_sum){
                left_sum=sum;
                max_left=i;
            }
        }

        right_sum=Integer.MIN_VALUE;
        sum=0;
        for(int i=mid+1;i<=high;i++){
            sum+=A[i];
            if(sum>right_sum){
                right_sum=sum;
                max_right=i;
            }
        }
    }
    public int max_left(){
        return max_left;
    }
    public int max_right(){
        return max_right;
    }
    public int max_sum(){
        return left_sum+right_sum;
    }

}

运行结果:
实验结果

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值