zoukankan      html  css  js  c++  java
  • 求连续子数组的最大和

    题目来源:《剑指offer》面试题31,《编程珠玑》第八章,LeetCode OJ的Maximum Subarray。

    题目大意:

    输入一个整型数组,数组里有正数也有负数。数组中一个或连续的多个整数组成一个子数组。求所有子数组的最大和。

    例如,数组为{1,-2,3,10,-4,7,2,-5},和最大的子数组为{3,10,-4,7,2},因此返回的结果为子数组的和18.

    来自Google,微软等很多知名公司的面试题,2005年浙江大学计算机系的考研题的最后一道程序设计题,貌似是风靡一时的一道题。

    参考链接:  程序员编程艺术:第七章、求连续子数组的最大和程序员面试题精选100题(03)-子数组的最大和

    解题思路:

    解法一:O(n3)

    最最简单粗暴的解法,就是列出数组的所有子集,然后一一计算总和,记录最大值。

    不多说,伪代码就是这样:

    maxsofar = 0;
    for i = [0,n)
        for j = [i,n)
            sum = 0;
            for k = [i,j]
                sum += x[k];
                //sum is sum of x[i...j]
                maxsofar = max(maxsofar,sum);
    View Code

    解法二:O(n2) 

    基本上就是在解法一的基础上,把最里面的那层循环消去。

    因为x[i...j]的总和与前面已计算出的总和(x[i...j-1]的总和)密切相关。

    maxsofar = 0;
    for i = [0,n)
        sum = 0;
        for j = [i,n)
            sum += x[j];
            //sum is sum of x[i...j]
            maxsofar = max(maxsofar,sum);
    View Code

    解法三:O(nlogn)

    分治算法。

    要解决规模为n的问题,可以递归的解决两个规模近似为n/2的子问题,然后对他们的答案进行合并以得到整个问题的答案。

    若m为中间的数,a为左边的集合,b为右边的集合,那么最大子集和要么在a中,要么在b中,要么横跨a和b包括了a的右边和b的左边。

    代码就这个:

    //动态规划
    int max_3(int a,int b,int c){
        if(a >= b){
            return a>=c?a:c;
        }
        else{
            return b>=c?b:c;
        }
                
    }
    int maxsum3(int *x,int l,int u){
        if(l > u)
            return 0;
        if(l == u)
            return x[l]>=0? x[l]:0;
        int m = (l+u)/2;
    
        int lmax,sum,rmax;
        lmax = sum = 0;
        for(int i = m; i >= l; i--){
            sum += x[i];
            if(sum > lmax)
                lmax = sum;
        }
    
        rmax = sum = 0;
        for(int i = m;i <= u; i++){
            sum += x[i];
            if(sum > rmax)
                rmax = sum;
        }
        int lr = lmax+rmax;
        int ll = maxsum3(x,l,m);
        int rr = maxsum3(x,m+1,u);
    
        return max_3(lr,ll,rr);
    }
    View Code

    解法四:O(n)

    扫描算法。(动态规划)

    • 设置两个辅助变量,curSum和greatestSum(在编程珠玑中为maxendinghere和maxsofar)。
    • 为了区别传入指针为空指针时返回0和正常计算最大值返回0的情况,我们需要添加一个全局变量invalidInput来标志。
    • 一个for循环遍历数组,i 表示当前下标,如果curSum < 0,我们就让curSum = pData[i],否则就让curSum += pData[i],因为当curSum小于0的时候,再加上pData[i]的结果必然比 pData[i] 小,不如将curSum清0,只包含pData[i] 一个元素。
    • 然后再用greatestSum每次跟curSum比较,记录下当前的最大值。

    《编程珠玑》的伪代码:

    maxsofar = 0;
    maxendinghere = 0;
    for i = [0,n)
        maxendinghere = max(maxendinghere + x[i], 0);
        maxsofar = max(maxsofar,maxendinghere);

    《剑指offer》的代码:

    bool invalidInput = false;
    int FindGreatestSumOfSubArray(int *pData,int nLength){
        if((pData == NULL) || nLength <= 0){
            invalidInput = true;
            return 0;
        }
        invalidInput = false;
    
        int curSum = 0;
        int greatestSum = 0x80000000;//-2147483648
        for(int i = 0; i < nLength; i++){
            if(curSum < 0)
                curSum = pData[i];
            else
                curSum += pData[i];
            if(curSum > greatestSum)
                greatestSum = curSum;
        }
        return greatestSum;
    }

    主函数:

    int main(){
        int a[] = {1,-2,3,10,-4,7,2,-5};
        int sum = FindGreatestSumOfSubArray(a,sizeof(a)/sizeof(int));
        //int sum = MaxSum(a,sizeof(a)/sizeof(int));
        //int sum = maxsum3(a,0,14);
        cout << sum << endl;
        getchar();
        return 0;    
    }
    View Code
  • 相关阅读:
    LeetCode——加油站
    LeetCode——分发糖果
    LeetCode——单词拆分 ii
    LeetCode—— 单词拆分
    LeetCode——重排链表
    IAR ARM、IAR STM8、IAR MSP430共用一个IDE
    OSAL多任务资源分配机制
    Win7系统Matlab2013a安装.m文件不自动关联到MATLAB.exe解决方法
    Java SE/ME/EE的概念介绍
    STL,ATL,WTL之间的联系和区别
  • 原文地址:https://www.cnblogs.com/4everlove/p/3653205.html
Copyright © 2011-2022 走看看