第五章、求连续子数组的最大和

本文介绍了求解子数组最大和的不同算法实现,包括暴力搜索、动态规划等方法,并提供了多种时间复杂度下的代码示例。

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

例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,因此输出为该子数组的和18。
思路一:求一个数组的最大子数组和,如此序列1, -2, 3, 10, -4, 7, 2, -5,我想最最直观也是最野蛮的办法便是,三个for循环三层遍历,求出数组中每一个子数组的和,最终求出这些子数组的最大的一个值。记Sum[i, …, j]为数组A中第i个元素到第j个元素的和(其中0 <= i <= j < n),遍历所有可能的Sum[i, …, j],那么时间复杂度为O(N^3)。
代码如下:

int MaxSum(int* A, int n)
        {
            int maximum = -INF;
            int sum = 0;
            for (int i = 0; i < n; i++)
            {
                for (int j = i; j < n; j++)
                {
                    for (int k = i; k <= j; k++)
                    {
                        sum += A[k];
                    }
                    if (sum > maximum)
                        maximum = sum;

                    sum = 0;   //这里要记得清零,否则的话sum最终存放的是所有子数组的和。也就是编程之美上所说的bug。
                }
            }
            return maximum;
        } 
  1. 思路二:例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,那么最大的子数组为3, 10, -4, 7, 2,因此输出为该子数组的和18。
    所有的东西都在以下两行,即:
    b  :  0  1  -1  3  13   9  16  18  13  
    sum:  0  1   1  3  13  13  16  18  18
    其实算法很简单,当前面的几个数,加起来后,b<0后,把b重新赋值,置为下一个元素,b=a[i]。当b>sum,则更新sum=b;若b<sum,则sum保持原值,不更新.
    代码如下:
    #include <iostream.h>
    using namespace std;
    
    int maxSum(int* a, int n)
    {
    	int sum=0;
    	int b=0;
    	
    	for(int i=0; i<n; i++)
    	{
    		if(b<0)           
    			b=a[i];
    		else
    			b+=a[i];
    		if(sum<b)
    			sum=b;
    	}
    	return sum;
    }
    
    int main()
    {
        int a[10]={1, -2, 3, 10, -4, 7, 2, -5};
        cout<<maxSum(a,8)<<endl;
        return 0;
    }
    由于在上述过程中没有考虑全是负数的情况,我们假设如果数组全部是负数的话,那么返回值就返回负数中的最大值。
    代码修改如下:
    #include <iostream.h>
    using namespace std;
    
    int maxsum(int *a,int n)  
    {
        int max=a[0];       //全负情况,返回最大数
        int sum=0;
        for(int j=0;j<n;j++)
        {
            if(sum>=0)     //如果加上某个元素,sum>=0的话,就加
                sum+=a[j];
            else   
                sum=a[j];  //如果加上某个元素,sum<0了,就不加
            if(sum>max)
                max=sum;
        }
        return max;
    }
    
    int main()
    {
        int a[]={-1,-2,-3,-4};
        cout<<maxsum(a)<<endl;
        return 0;
    }

    思路三:DP解法的具体过程:设sum[i] 为前i个元素中,包含第i个元素且和最大的连续子数组,result 为已找到的子数组中和最大的。对第i+1个元素有两种选择:做为新子数组的第一个元素、放入前面找到的子数组。
    sum[i+1] = max(a[i+1], sum[i] + a[i+1]);result = max(result, sum[i]);
    代码如下:
    #include <iostream.h>
    using namespace std;
    
    int maxsum(int *a,int n)  
            {
                int *sum = new int[n];
                int result=a[0];
                int sum[0] = a[0];
                for(int j=1;j<n;j++)
                {
                    sum[j] = max(a[j],sum[j] + a[j]);      //max(a,b),返回a,b中大的数
                    result = max(reslut,sum[j]);
                }
                delete[] sum;
                return result;
            }
    
    int main()
    {
        int a[]={-1,-2,-3,-4};
        cout<<maxsum(a)<<endl;
        return 0;
    }


    第二节、Data structures and Algorithm analysis in C

    下面给出《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 Case
    		if(A[Left]>0)
    			return A[Left];
    		else
    			return 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)
    //同上述第一节中的思路2、和3。
    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;
    }

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值