算法优化之计算最大子序列的和问题

问题:给定(可能有负的)整数A1,A2,A3.........An,求子序列的最大值。(为了方便起见,如果所有整数均为负数,则最大子序列的和为0)

解释下题目:子序列为连续的序列,比如有2,-1,4,3,5,-2,7,4,-8;子序列-1,4,3,5的和为11.则最大子序列为2,-1,4,3,5,-2,7,4.

这个问题有很多种解法,下面列举几种:

for循环穷举所有的可能

public static int maxSubSum(int [] a){
        int maxSum = 0;
        for (int i=0;i<a.length;i++){
            for (int j = i; j <a.length ; j++) {
                int thisSum = 0;
                for (int k = i; k <=j ; k++){

                    thisSum +=a[k];
                }
                if(thisSum>maxSum){
                    maxSum = thisSum;
                }
            }
        }
        return maxSum;
 }

算法很简单,就是使用for循环穷举可能出现的子序列和,然后求最大值。

使用递归二分查找

public static int maxSubSum2(int[] a){
        return maxSumRec(a,0,a.length-1);
    }

    private static int maxSumRec(int[] a, int left, int right) {
        if(left==right){
            //base case递归基例
            if(a[left]>0){
                return a[left];
            }else{
                return 0;
            }
        }
        
        int center  = (left+right)/2;
        int maxLeftSum = maxSumRec(a,left,center);
        int maxRightSum = maxSumRec(a,center+1,right);
        
        int maxLeftBorderSum = 0,leftBorederSum = 0;
        for (int i = center; i >=left; i--) {
            //求左半边的最大子序列和
            maxLeftBorderSum += a[i];
            if(leftBorederSum>maxLeftBorderSum){
                maxLeftBorderSum = leftBorederSum;
            }
        }
        
        int maxRightBorferSum=0, rightBorderSum=0;
        //求右半边子序列的最大和
        for (int i = center+1; i <=right; i++) {
            rightBorderSum += a[i];
            if(rightBorderSum>maxRightBorferSum){
                maxRightBorferSum = rightBorderSum;
            }

        }
        //返回左半边子序列、右半边子序列,左右对接子序列中的最大值
        return max(maxLeftSum,maxRightSum,maxLeftBorderSum+maxRightBorferSum);
    }

算法使用了递归和二分法。先使用二分法将序列从中间分为左右两个子序列,那么最大的子序列的和的值出现在左边子序列或者右边子序列,或者左边子序列的子序列和右边子序列的序列的拼接。使用递归找出左边子序列和右边子序列的值,for循环找出左边子序列与右边子序列拼接的最大和值,然后三个值进行比较,最大的值即为序列的最大子序列和。

与第一种方法对比,减少了嵌套for循环,优化了计算次数。

比较精妙的算法

下面介绍一种从书上看来的比较精妙的算法,代码很简单,但是逻辑理解起来却不是那么容易。

public static int maxSubSum3(int[] a){
        int maxSum = 0,thisSum = 0;
        for (int i = 0; i <a.length ; i++) {
            thisSum += a[i];
            if(thisSum>maxSum){
                maxSum=thisSum;
            }else if(thisSum<0){
                thisSum=0;
            }
        }
        
        return maxSum;
    }

代码与前面两种算法比较起来算是很简单了,但是逻辑理起来却需要思考一会。算法的大概思想是:从第一个数开始加,如果和大于定义的maxSum就讲这个值赋值给maxSum,如果序列和为负数,就将序列和置为0。个人理解这个算法的关键在于当依次相加的序列的和为负数的时候就将这个值置为0,从新开始求和,因为负数与正数相加肯定是小于这个正数的,那么这个序列后面的数字再相加肯定小于从0开始重新求和的值。

结论

从上面的例子可以看出,因为算法的不同导致代码对相同的求解执行的时间差异很大,代码的量差异也很大,对性能的影响也比较大。比较好的算法花费的考虑时间也相对更长一些。so,这就是算法的力量。




猜你喜欢

转载自blog.csdn.net/fengyibande/article/details/80316541