最大连续子数组和

来源:互联网 发布:算法第四版课后题答案 编辑:程序博客网 时间:2024/05/18 18:54

题目描述

输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。 求所有子数组的和的最大值,要求时间复杂度为O(n)。

例如输入的数组为 1, -2, 3, 10, -4, 7, 2, -5 ,和最大的子数组为 3, 10, -4, 7, 2 , 因此输出为该子数组的和18。


抛出Java代码,暴利扫描法O(n3)  和 正确解题法 O(n)

public class MaxSubArray {public static int getMaxSubArray(int[] arr ,int n){int maxSum = arr[0];int curSum = 0;for(int i = 0; i<n; i++){for(int j=i; j<n; j++){for(int k=i; k<=j; k++){curSum += arr[k];}if(curSum > maxSum){maxSum = curSum;}curSum = 0;}}return maxSum;}public static int getMaxSubArr(int[] arr,int n) {int curSum = 0;int maxSum = arr[0];for(int j=0; j<n; j++){curSum = (arr[j] > curSum + arr[j]) ? arr[j] : curSum + arr[j];maxSum = (maxSum > curSum) ? maxSum : curSum;}return maxSum;}public static void main(String[] args) {int[] array = new int[]{1,-2,3,10,-4,7,2,-5};int sum1 = getMaxSubArray(array,array.length);int sum = getMaxSubArr(array, array.length);System.out.println("最大子数组和:" + sum);}}



看到O(n)时间复杂度,就应该能够想到我们只能对整个数组进行一次扫描,在扫描过程中求出最大连续子序列和以及子序列的起点和终点位置。假如输入数组为{1,-2,3,10,-4,7,2,-5},我们尝试从头到尾累加其中的正数,初始化和为0,第一步加上1,此时和为1,第二步加上-2,此时和为-1,第三步加上3,此时我们发现-1+3=2,最大和2反而比3一个单独的整数小,这是因为3加上了一个负数,发现这个规律以后我们就重新作出累加条件:如果当前和为负数,那么就放弃前面的累加和,从数组中的下一个数再开始计数

#include <iostream>using namespace std;int MaxSubArray(int array[],int len){    if(array==NULL || len<=0)        return -1;    int start=0,end=0;    int curSum=0;    int maxSum=array[0];    for(int i=0; i<len; i++)    {        if(curSum<0) //如果当前最大和为负数,则舍弃前面的负数最大和,从下一个数开始计算        {            curSum=array[i];            start=i;        }        else            curSum += array[i];        if(curSum>maxSum)        {            maxSum=curSum;            end=i;        }        cout << "curSum:" << curSum << "  maxSum:" << maxSum << endl;    }    cout<<"最大子序列位置:"<<start<<"--"<<end<<endl;    return maxSum;}int main(){    int array[]={1,-2,3,10,-4,7,2,-5};    int len=sizeof(array)/sizeof(int);    int maxsum=MaxSubArray(array,len);    cout << "最大子序列和:" << maxsum << endl;}

下面给出《Data structures and Algorithm analysis in C》中4种实现。

//Algorithm 1:时间效率为O(n*n*n)int MaxSubsequenceSum1(const int A[],int N){int ThisSum=0 ,MaxSum=0,i,j,k;for(i=0;i<N;i++)for(j=i;j<N;j++){ThisSum=0;for(k=i;k<j;k++)ThisSum+=A[k];if(ThisSum>MaxSum)MaxSum=ThisSum;}return MaxSum;}//Algorithm 2:时间效率为O(n*n)int MaxSubsequenceSum2(const int A[],int N){int ThisSum=0,MaxSum=0,i,j,k;for(i=0;i<N;i++){ThisSum=0;for(j=i;j<N;j++){ThisSum+=A[j];if(ThisSum>MaxSum)MaxSum=ThisSum;}}return MaxSum;}//Algorithm 3:时间效率为O(n*log n)//算法3的主要思想:采用二分策略,将序列分成左右两份。//那么最长子序列有三种可能出现的情况,即//【1】只出现在左部分.//【2】只出现在右部分。//【3】出现在中间,同时涉及到左右两部分。//分情况讨论之。static int MaxSubSum(const int A[],int Left,int Right){int MaxLeftSum,MaxRightSum;              //左、右部分最大连续子序列值。对应情况【1】、【2】int MaxLeftBorderSum,MaxRightBorderSum;  //从中间分别到左右两侧的最大连续子序列值,对应case【3】。int LeftBorderSum,RightBorderSum;int Center,i;if(Left == Right)Base Caseif(A[Left]>0)return A[Left];elsereturn 0;Center=(Left+Right)/2;MaxLeftSum=MaxSubSum(A,Left,Center);MaxRightSum=MaxSubSum(A,Center+1,Right);MaxLeftBorderSum=0;LeftBorderSum=0;for(i=Center;i>=Left;i--){LeftBorderSum+=A[i];if(LeftBorderSum>MaxLeftBorderSum)MaxLeftBorderSum=LeftBorderSum;}MaxRightBorderSum=0;RightBorderSum=0;for(i=Center+1;i<=Right;i++){RightBorderSum+=A[i];if(RightBorderSum>MaxRightBorderSum)MaxRightBorderSum=RightBorderSum;}int max1=MaxLeftSum>MaxRightSum?MaxLeftSum:MaxRightSum;int max2=MaxLeftBorderSum+MaxRightBorderSum;return max1>max2?max1:max2;}//Algorithm 4:时间效率为O(n)//同上述第一节中的思路3、和4。int MaxSubsequenceSum(const int A[],int N){int ThisSum,MaxSum,j;ThisSum=MaxSum=0;for(j=0;j<N;j++){ThisSum+=A[j];if(ThisSum>MaxSum)MaxSum=ThisSum;else if(ThisSum<0)ThisSum=0;}return MaxSum;} 




参考:

http://blog.csdn.net/v_JULY_v/article/details/6444021

http://www.cnblogs.com/xwdreamer/archive/2012/05/04/2482507.html








0 0