《剑指Offer》面试题:连续子数组的最大和

题目描述:

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天JOBDU测试组开完会后,他又发话了:在古老的一维模式识别中,
常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。
但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?

有如下几种思路
1、暴力搜索法:先找出从第1个元素开始的最大子数组,而后再从第2个元素开始找出从第2个元素开始的最大子数组,依次类推,比较得出最大的子数组。但是该方法的时间复杂度为O(n*n);

实现如下

/*
思路:暴力搜索法: 
*/

#include<stdio.h>
#include<stdlib.h>
//求数组arr从begin到end之间元素的和
int sum(int *arr,int begin,int end){
    if(arr!=NULL&&begin>=0&&begin<=end){
        int result=0;
        for(int i=begin;i<=end;i++){
            result+=arr[i];
        }
        return result;
    }
    else{
        return -1;
    }
} 
void maxSumInSubArray(int *arr,int len){
    if(arr==NULL||len<=0){
        return;
    }
    int finalMaxSum=arr[0];
    int finalBegin=0;
    int finalEnd=0;
    for(int j=0;j<len-1;j++){
        int curMaxSum=arr[j]; 
        int begin=j;//在数组中最终结果的 起点 
        int end=j;//在数组中最当前的终点 
        int beginIndex=j+1; 
        for(int i=j+1;i<len;i++){//找出从第 j个元素开始的最大子数组和。      
            if(sum(arr,beginIndex,i)>=0){//直到找到从起点开始的一段连续子数组大于零,然后将此段子数组的区间加入最后的结果中。 
                    end=i;
                    beginIndex=end+1;//准备后续的判断 
            }

        }
        //到这里就是求出了从j个元素为起点的最大子数组和区间 
            curMaxSum=sum(arr,begin,end);
            if(curMaxSum>finalMaxSum){
                finalMaxSum=curMaxSum; 
                finalBegin=begin;
                finalEnd=end;
            }   
    }


    //经过上面的处理,则数组从begin到end即为我们所求的结果。接下来输出即可。
    printf("%d  %d   %d",finalMaxSum,finalBegin,finalEnd);

}
int main(void){
    int n;
    while(scanf("%d",&n)!=EOF&&n>=0&&n<=100000) {
        if(n==0){
            break;
        }
        int *arr=(int *)malloc(n*sizeof(int));
        if(arr==NULL){
            exit(EXIT_FAILURE);
        }
        for(int i=0;i<n;i++){
            int val;
            scanf("%d",&val);
            *(arr+i)=val;

        }
        maxSumInSubArray(arr,n);
        printf("\n");
    }
    return 0;
}

思路2

在每次元素累加和小于0时,从下一个元素重新开始累加。时间复杂度为O(n)

/*
题目描述:
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天JOBDU测试组开完会后,他又发话了:在古老的一维模式识别中,
常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。
但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?

输入:
输入有多组数据,每组测试数据包括两行。
第一行为一个整数n(0<=n<=100000),当n=0时,输入结束。接下去的一行包含n个整数(我们保证所有整数属于[-1000,1000])。
输出:
对应每个测试案例,需要输出3个整数单独一行,分别表示连续子向量的最大和、该子向量的第一个元素的下标和最后一个元素的下标。若是存在多个子向量,则输出起始元素下标最小的那个。
样例输入:
3
-1 -3 -2
5
-8 3 2 0 5
8
6 -3 -2 7 -15 1 2 2
0
样例输出:
-1 0 0
10 1 4
8 0 3
*/


#include<stdio.h>
#include<stdlib.h>
void maxSumInSubArray(int *arr,int len){
    if(arr==NULL||len<=0){
        return;
    }
    int curBegin=0;//当前起点 
    int curEnd=0;//在数组中最当前的终点 
    int curSum=arr[0];
    //保存最终的结果。 
    int finalBeginIndex=0;
    int finalEndIndex=0; 
    int finalSum=arr[0];

    //从前到后开始检测
    for(int i=1;i<len;i++){

        if(curSum<0){//如果当前和小于0,则舍弃,否则,继续向后累加, 
            curSum=arr[i];
            curBegin=i;
            curEnd=i;
        }
        else{
            curSum+=arr[i];
            curEnd=i;
        }
        if(curSum>finalSum){//当当前和大于finalSum时,立即更新 
            finalBeginIndex=curBegin;
            finalEndIndex=curEnd;
            finalSum=curSum;
        }

    }

    //经过上面的处理,则数组从begin到end即为我们所求的结果。接下来输出即可。
    printf("%d  %d   %d",finalSum,finalBeginIndex,finalEndIndex);

}
int main(void){
    int n;
    while(scanf("%d",&n)!=EOF&&n>=0&&n<=100000) {
        if(n==0){
            break;
        }
        int *arr=(int *)malloc(n*sizeof(int));
        if(arr==NULL){
            exit(EXIT_FAILURE);
        }
        for(int i=0;i<n;i++){
            int val;
            scanf("%d",&val);
            *(arr+i)=val;

        }
        maxSumInSubArray(arr,n);
        printf("\n");
    }
    return 0;
}

运行结果如下:

最近在复习,在牛客网上的AC代码如下:

   public int FindGreatestSumOfSubArray(int[] array) {
        if(array==null||array.length==0){
            return 0;
        }
        int len = array.length;
        int curSum = array[0];
        int beginIndex = 0;
        int endIndex = 0;
        int finalSum = array[0];
        int resBeginIndex = 0;
        int resEndIndex = 0;
        for(int i=1;i<len;i++){
            if(curSum<0){
                curSum = array[i];
                beginIndex = i;
                endIndex = i;
            }
            else{
                curSum+=array[i];
                endIndex = i;
            }
            if(curSum>finalSum){
                finalSum = curSum;
                resBeginIndex  = beginIndex;
                resEndIndex = endIndex;
            }
        }
        return finalSum;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值