关于求解最大子序列和问题的总结

最近看了求解最大子序列和的几种算法,为了巩固自己的算法基础,特整理一下,并分析相关算法的复杂度,理清自己的思路。
最大子序列和问题描述:对于序列a[N] 来说,求其 jk=ia[k] 的最大和。
算法一 :基于穷举的思想,将所有的a[k]全部遍历一边,每个子序列的起点均为 i ,终点为j,然后找出最大和的子序列即可。

//求最大子序列和的函数,穷举1。
int maxSubQueneSum(int [] a){
    int maxSubSum = 0, subSum = 0;
    for(int i = 0; i < a.length; i++){
        for(int j = i; j < a.length; j++){
            for(int k = i; k <= j; k++){
                subSum = subSum + a[k];
            }
            if(subSum > maxSubSum)
                maxSubSum = subSum;
        }
    }
    return maxSubSum;
}

该算法利用了三个for循环嵌套来达到遍历穷搜的目的,其复杂度为 O(N3) ,复杂度较高。
算法二:该算法基于上面的算法一去掉内部的第三层for循环,是基于 jk=ia[k]=a[j]+j1k=ia[k] 的思想,因为算法一有许多重复的计算,例如:计算a[1]+a[2]和计算a[1]+a[2]+a[3]均重复计算了a[1]+a[2]。而算法二会去除这样的重复。

//求最大子序列和的函数2
    int maxSubQueneSum2(int [] a){
        int maxSubSum = 0, subSum = 0;
        for(int i = 0; i < a.length; i++){
            for(int j = i; j < a.length; j++){
                subSum = subSum + a[j];
                if(subSum > maxSubSum)
                    maxSubSum = subSum;
            }
        }
        return maxSubSum;
    }

很容易分析出该算法的复杂度为 O(N2)
上面的两种算法都是基于穷搜而得到的。由上面的两个算法我能够想到一句很好的话:计算任何事情不要超过一次。觉得这个思想很受用。
算法三:算法三就有一定的技巧性了,利用的是分治的思想,分而治之,也就是说将序列人为地从中间一分为二,则其最大和子序列必存在于三个位置,一个是位于左半部的子序列,一个是位于右半部的子序列,一个是位于在左半部和右半部之间过渡的子序列,左半部和右半部子序列又可以分而治之,继续分割直至为一个元素为止。对于过渡位置的子序列可以如下求出:
对于左半部来说,以左半部最后一个元素为起点向左寻找,找出最大子序列和;同理对于右半部子序列,以第一个元素为起点向右寻找,找出最大子序列和。两者相加即可求出过渡位置的最大子序列和。依次递归调用即可。

//求最大子序列和的函数3
    int maxSubQueneSum3(int [] a, int left, int right){
        //递归的终止条件
        if(left == right) 
            return a[left];
        int center = (left + right)/2;
        //求左半部子序列和
        int maxLeftSubBoundSum = maxSubQueneSum3(a, left, center);
        //计算右半部子序列和
        int maxRightSubBoundSum = maxSubQueneSum3(a,center+1,right);
        //计算中间过渡部分的最大子序列和
        int leftBoundSum = 0, maxLeftBoundSum = 0;
        for(int i = center; i<= left; i--){
            leftBoundSum = leftBoundSum + a[i];
            if(leftBoundSum > maxLeftBoundSum)
                maxLeftBoundSum = leftBoundSum;
        }
        int rightBoundSum = 0, maxRightBoundSum = 0;
        for(int i = center; i<= left; i--){
            rightBoundSum = rightBoundSum + a[i];
            if(rightBoundSum > maxRightBoundSum)
                maxRightBoundSum = rightBoundSum;
        }
        int maxMidSubSum = maxLeftBoundSum + maxRightBoundSum;
        //求出三者中最大的一个子序列和
        return max(maxLeftSubBoundSum, maxRightSubBoundSum, maxMidSubSum);
    }


    private int max(int a, int b, int c) {
        // TODO 自动生成的方法存根
        return a>b?(a>c?a:b):(b>c?b:c);
    }

该算法通过递归调用而得,设该函数本身的花费为 T(N) ,出去常数项花费,求左半部分的最大子序列和花费为 T(N/2) 右半部分的最大子序列和花费为 T(N/2) ,在计算中间过渡部分的最大子序列和是会将N个数全部遍历一边,所以花费为 O(N) ,因此总的花费为 T(N)=2T(N/2)+O(N)
计算复杂度
所以复杂度为 O(Nlog2N)
算法四: 该算法取的是这么一种思想:如果 a[i] 是负数,则最大子序列和绝不会是以 a[i] 开头的,同理如果 a[i] a[j] a[j] 是使得 a[i] a[j] 为负的第一个数)和为负数,则 a[i] a[j] 不可能是最大子序列和的前缀,所以可以将最大子序列和的起始位置推到 a[j+1] 。基于这一原理,可得:

    //求最大子序列和的函数4
    int maxSubQueneSum4(int [] a){
        int maxSum = 0, sum = 0;
        for(int i = 0; i < a.length; i++){
            sum = sum + a[i];
            if(sum > maxSum){
                maxSum = sum;
            }
            else
                sum = 0;
        }
        return maxSum;
    }

由上可见,该算法有点聪明,但是总感觉有点投机取巧之嫌,其复杂度为 O(N) ,是线性复杂度,又由于该算法总是能计算出当前序列的最大子序列和,无需在整个序列输入完以后才计算,所以又具有“联机算法”的特性。
但是我有点不明白这算法不能计算当 a[N] 都为负数时最大子序列和,哪位高人能够讲一讲?小弟感激不尽,还有对于算法三如何定位最大子序列和的位置?

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值