三种算法求解一个数组的子数组最大和

题目:要求一个数组连续下标和的最大值,数组的元素可正、可负、可为零,例如-2,5,3,-6,4,-8,6将返回8。

这题是很经典的一道面试题,也有各种解法,从算法分析上,时间复杂度也有很大差别,下面我就给出三种不同的解法。

方法一:暴力枚举法

此种方法最简单,我想应该也是每个人拿到题目想到的第一种解法了,学过一点编程的人都应该能编出此类程序。
记sum[i..j]为数组中第i个元素到第j个元素的和(其中0<=i<j<=n-1),通过遍历所有的组合之和,就能找到最大的一个和了。
伪代码如下:
int maxSubArray(int *A,int n) {
    int maxium = -INF; //保存最大子数组之和
    for i=0 to n-1 do
        sum = 0; //sum记录第i到j的元素之和
        for j=i to n-1 do
            sum += A[j];
        if sum>maxium do //更新最大值
            maxium = sum;
    return maxium;
}
此种方法的时间 复杂度为O(n2),显然不是一种很好的办法,也不是公司面试希望你写出这样的程序的。

方法二:分支界定
这里再介绍一种更高效的算法,时间 复杂度为O(nlogn)。这是个分治的思想,解决复杂问题我们经常使用的一种思维方法——分而治之。
而对于此题,我们把数组A[1..n]分成两个相等大小的块:A[1..n/2]和A[n/2+1..n],最大的子数组只可能出现在三种情况:
    A[1..n]的最大子数组和A[1..n/2]最大子数组相同;
    A[1..n]的最大子数组和A[n/2+1..n]最大子数组相同;
    A[1..n]的最大子数组跨过A[1..n/2]和A[n/2+1..n]
前两种情况的求法和整体的求法是一样的,因此递归求得。
第三种,我们可以采取的方法也比较简单,沿着第n/2向左搜索,直到左边界,找到最大的和maxleft,以及沿着第n/2+1向右搜索找到最大和maxright,那么总的最大和就是maxleft+maxright。
而数组A的最大子数组和就是这三种情况中最大的一个。
伪代码如下:
int maxSubArray(int *A,int l,int r) {
    if l<r do
        mid = (l+r)/2;
        ml = maxSubArray(A,l,mid); //分治
        mr = maxSubArray(A,mid+1,r);
        for i=mid downto l do
            search maxleft;
        for i=mid+1 to r do
            search maxright;
        return max(ml,mr,maxleft+maxright); //归并
        then //递归出口
            return A[l];
}
方法三:动态规划
这算是一个经典的动态规划的题目了,如果不知道动态规划可以先不去理解这个名词。用通俗点的语言描述这个算法就是:
令cursum(i)表示数组下标以i为起点的最大连续下标最大的和,而maxsum(i)表示前i个元素的最大子数组之和。那么我们就可以推出下一个maxsum(i+1)应该为cursum(i+1)和maxsum(i)中选取一个最大值。递推式为:
cursum(i) = max{A[i],cursum(i-1)+A[i]};
maxsum(i) = max{maxsum(i-1),cursum(i+1)};
伪代码为:
int maxSubArray(int *A,int n) {
    cursum = A[0];
    maxsum = A[0];
    for i=1 to n-1 do
        /*当我们加上一个正数时,和会增加;当我们加上一个负数时,和会减少。如果当前得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零,不然的话这个负数将会减少接下来的和。*/
        if cursum<0 do
            cursum = 0;
        cursum += A[i];
        if cursum>maxsum do
            maxsum = cursum;
    return maxsum;
}
这种算法时间复杂度只是O(n),效果非常好!
具体实现代码如下:
#include <iostream>
#include <iterator>
#include <algorithm>
#include <cstdlib>
#include <ctime>
using  namespace std;

const  int INF= 0x7fffffff;
int max_sub_array( int arr[], int n, int &left, int &right)
{
     int maxium=-INF;
     int sum;
     for( int i= 0;i<n;i++){
        sum= 0;
         for( int j=i;j<n;j++){
            sum+=arr[j];
             if(sum>maxium){
                maxium=sum;
                left=i;
                right=j;
            }
        }
    }

     return maxium;
}
int max_sub_array( int arr[], int l, int r, int &left, int &right)
{
     if(l<r){
         int mid=(l+r)/ 2;
         int ll,lr;
         int suml=max_sub_array(arr,l,mid,ll,lr);
         int rl,rr;
         int sumr=max_sub_array(arr,mid+ 1,r,rl,rr);
         int sum_both= 0;
         int max_left=-INF;
         int ml,mr;
         for( int i=mid;i>=l;i--)
        {
            sum_both+=arr[i];
             if(sum_both>max_left){
                max_left=sum_both;
                ml=i;
            }
        }
         int max_right=-INF;
        sum_both= 0;
         for( int i=mid+ 1;i<=r;i++)
        {
            sum_both+=arr[i];
             if(sum_both>max_right){
                max_right=sum_both;
                mr=i;
            }
        }
        sum_both=max_left+max_right;
         if(suml<sumr) {
             if(sumr<sum_both) {
                left=ml;
                right=mr;
                 return sum_both;
            }
             else {
                left=rl;
                right=rr;
                 return sumr;
            }

        }
         else{
             if(suml<sum_both) {
                left=ml;
                right=mr;
                 return sum_both;
            }
             else {
                left=ll;
                right=lr;
                 return suml;
            }

        }
    }
     else {
        left=l;
        right=r;
         return arr[l];
    }
}
int max_sub_array_( int arr[], int n, int& left, int&right)
{
     int cursum=arr[ 0];
     int maxsum=arr[ 0];
     int pos= 0;
    pos= 0;
     for( int i= 1;i<n;i++) {
//         if(cursum<0)
//             cursum=0;
        cursum+=arr[i];
         if(cursum<arr[i])
        {
            pos=i;
            cursum=arr[i];
        }
         if(cursum>maxsum)
        {
            maxsum=cursum;
            left=pos;
            right=i;
        }
    }
     return maxsum;

}
void test1()
{
     int arr[]={- 2, 5, 3,- 6, 4,- 8, 6};
     int len= sizeof(arr)/ sizeof(arr[ 0]);
     int left,right;
     int sum;
    cout<< " arr: ";
    copy(arr,arr+len,ostream_iterator< int>(cout,  "   "));
    cout<<endl;
    sum=max_sub_array(arr,len,left,right);
    cout<< " method1:( "<<left<< " , "<<right<< " )   ";
    cout<< " sum= "<<sum<<endl;
    sum=max_sub_array(arr, 0,len- 1,left,right);
    cout<< " method2:( "<<left<< " , "<<right<< " )   ";
    cout<< " sum= "<<sum<<endl;
    sum=max_sub_array(arr,len,left,right);
    cout<< " method3:( "<<left<< " , "<<right<< " )   ";
    cout<< " sum= "<<sum<<endl;

}
void test2()
{
     const  int LEN= 10;
     int arr[LEN];
     int sign[LEN];
    srand(time( 0));
     for( int i= 0;i<LEN;i++){
         int val=rand()% 1000;
         if(val% 2== 0)
            sign[i]= 1;
         else
            sign[i]=- 1;
    }
     for( int i= 0;i<LEN;i++){
         int val=rand()% 100;
        arr[i]=val*sign[i];
    }
     int left,right;
     int sum;
     int len=LEN;
    cout<< " arr: ";
    copy(arr,arr+len,ostream_iterator< int>(cout,  "   "));
    cout<<endl;
    sum=max_sub_array(arr,len,left,right);
    cout<< " method1:( "<<left<< " , "<<right<< " )   ";
    cout<< " sum= "<<sum<<endl;
    sum=max_sub_array(arr, 0,len- 1,left,right);
    cout<< " method2:( "<<left<< " , "<<right<< " )   ";
    cout<< " sum= "<<sum<<endl;
    sum=max_sub_array(arr,len,left,right);
    cout<< " method3:( "<<left<< " , "<<right<< " )   ";
    cout<< " sum= "<<sum<<endl;


}
int main()
{
    test2();
}

 其中test1函数是题目给出的数组测试,test2是进行随机产生数来测试。

大致的效果如下:

test1测试:

 

test2测试:

 

 

参考:http://www.cnblogs.com/hazir/archive/2011/05/08/2447289.html

 

 

 

 

转载于:https://www.cnblogs.com/xkfz007/archive/2012/05/17/2506299.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
分治算法是一种将问题划分为更小的问题然后合并问题的方法。在这个问题中,我们可以使用分治算法来找到一个数组最大数组。 首先,将原始数组分为更小的问题。我们可以从中间位置将数组分成两个数组:左数组和右数组。然后,我们可以递归地将左数组和右数组分别划分为更小的问题,直到问题规模变得足够小。 在每个问题中,我们需要找到最大数组。为了找到最大数组,我们可以使用经典的"最大数组问题"解决方法,即跨越中点的最大数组,或者不跨越中点的最大数组。我们可以计算出每个数组最大数组,并将它们与中点的最大数组进行比较。最后,我们返回最大数组作为结果。 以下是使用Java编写的分治算法求解数组最大数组: ```java public class MaxSubarray { public static int[] findMaxSubarray(int[] nums) { return findMaxSubarray(nums, 0, nums.length - 1); } private static int[] findMaxSubarray(int[] nums, int low, int high) { if (low == high) { return new int[]{low, high, nums[low]}; } else { int mid = (low + high) / 2; int[] leftSubarray = findMaxSubarray(nums, low, mid); int[] rightSubarray = findMaxSubarray(nums, mid + 1, high); int[] crossingSubarray = findMaxCrossingSubarray(nums, low, mid, high); if (leftSubarray[2] >= rightSubarray[2] && leftSubarray[2] >= crossingSubarray[2]) { return leftSubarray; } else if (rightSubarray[2] >= leftSubarray[2] && rightSubarray[2] >= crossingSubarray[2]) { return rightSubarray; } else { return crossingSubarray; } } } private static int[] findMaxCrossingSubarray(int[] nums, int low, int mid, int high) { int leftSum = Integer.MIN_VALUE; int sum = 0; int maxLeft = mid; for (int i = mid; i >= low; i--) { sum += nums[i]; if (sum > leftSum) { leftSum = sum; maxLeft = i; } } int rightSum = Integer.MIN_VALUE; sum = 0; int maxRight = mid + 1; for (int i = mid + 1; i <= high; i++) { sum += nums[i]; if (sum > rightSum) { rightSum = sum; maxRight = i; } } return new int[]{maxLeft, maxRight, leftSum + rightSum}; } } ``` 以上就是使用Java编写的分治算法求解数组最大数组。该算法具有良好的时间复杂度,并且可以有效地找到一个数组最大数组
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值