分治法-最大子段和

算法思想:分治法

实际问题:最大子段和

编写语言:Java

问题描述

此篇博文是分治法解决最大子段和问题的实现。 问题描述:给定由n个整数(可能为负数)组成的序列A={a1, a2, …, an},求该序列形如sum(A, i, j)的子段和的最大值。当所有整数均为负整数时,定义其最大子段和为0,依次定义,所求的最大值为:max{0, sum(A, i, j)}, 例如:(a1, a2, a3, a4, a5, a6)=(-2, 11, -4, 13, -5, -2)时,最大子段和为sum(A, 2, 4)=20 算法思想: 1. sum(A, 1, n)==sum(A, 1, n/2) 2. sum(A, 1, n)==sum(A, n/2+1, n) 3. sum(A, 1, n)==sum(A, i, j), 其中 1<=i<=n/2, n/2+1<=j<=n 解释:即序列A的最大子段和可能在A的左半部分,也可能在A的右半部分,还可能跨越了A的左右两个部分

Java代码

public class MaxSubsequenceSum
{
    public static void main(String[] args)
    {
        int[] a = new int[]{-2, 11, -4, 13, -5, -2};
        int result = maxSubSum(a, 0, a.length - 1);
        System.out.println("maxSubSum(a) = " + result);
    }
    
    /**
      * 使用分治思想求取最大子段和
      * 参数含义:
      *     a: 待求取最大子段和的数组
      *     left:子段起点
      *     right:子段终点
    */
    public static int maxSubSum(int[] a, int left, int right)
    {
        int sum = 0; //sum为总的最大子段和
        if(left == right)
            sum = a[left] > 0 ? a[left] : 0;
        else
        {
            int mid = (left + right) / 2;
            /*
             * 分治求解
            */
            //求左子段的和
            int leftSum = maxSubSum(a, left, mid);
            //求右子段的和
            int rightSum = maxSubSum(a, mid + 1, right);
            //求跨越左右两段的子段和:开始
            int maxLefts = 0;
            int lefts = 0;
            /*
             * 子段是连续的,从中间向两边扩散
             * 若是从左半部分从左边开始计算子段和,可能会导致整体的
             * 左右子段不连续,故左半部分子段和从右边往左边运算,
             * 右半部分子段和从左边往右边运算,保证整体的子段连续
            */
            for(int i = mid; i >= left; i--)
            {
                lefts += a[i];
                if(lefts > maxLefts)
                    maxLefts = lefts;
            }
            int maxRights = 0;
            int rights = 0;
            for(int i = mid + 1; i < right; i++)
            {
                rights += a[i];
                if(rights > maxRights)
                    maxRights = rights;
            }
            sum = maxLefts + maxRights;
            //求跨越左右两段的子段和:结束
            
            //判断得到最大子段和
            if(sum < leftSum)
                sum = leftSum;
            if(sum < rightSum)
                sum = rightSum;
        }
        return sum;
    }
}

运行结果

结果示例