【面试题之算法部分】最大和连续子数组

问题描述:给定一个数组A[0,1…n-1],求A的连续子数组,使该数组和最大

一. 暴力法
分析:首先初始化要求的最大值maxSum为A[0],然后定义三个索引i、j、k,然后三层循环:第一层i从0遍历到n-1,第二层j从i遍历到n-1,第三层k从i遍历到j,求出A[i]到A[j]之间的元素的和,然后和maxSum作比较并更新maxSum。

复杂度:时间复杂度为O(n^3),空间复杂度O(1)

代码:

int MaxSubArray(int A[], int n)
{
    int maxSum = A[0];
    int currSum;
    for(int i = 0; i < n; i++)
    {
        for(int j = i; j < n; j++)
        {
            currSum = 0;
            for(int k = i; k <=j; k++)
            {
                currSum += A[k];
            }
            if(currSum > maxSum)
                maxSum = currSum;
        }
    }
    return maxSum;
}

二. 暴力法优化版本
分析:在第一个版本中,由于在第二层循环遍历过程中,如果j增加1变为j+1,k从i到j又要重复算一次,其实如果优化一下,将前面的从A[i]到[j]计算的和保留下来,而此时遍历到j+1时,只需要用前面的保留值再加上A[j+1]即得到了从i到j+1的部分子数组和。

复杂度:时间复杂度O(n^2),空间复杂度O(1)

代码:

int MaxSubArray(int A[], int n)
{
    int maxSum = A[0];
    int currSum;
    for(int i = 0; i < n; i++)
    {
        currSum = 0;
        for(int j = i; j < n; j++)
        {
            currSum += A[j];
            if(currSum > maxSum)
            {
                maxSum = currSum;
            }
        }
    }
    return maxSum;
}

三. 分治法
分析:讲数组从中间分开,则最大子数组要么完全在组半边数组,要么在有半边数组,要么跨立在中间的分界点上,如果完全在左右半边数组,用递归解决,如果跨立在分界点上,则一定包含左半边数组的最大后缀和右半边数组的最大前缀,因此可以从分界处向前后扫。

复杂度:时间复杂度O(nlogn),空间复杂度O(1)

代码:

int MaxSubArray(int A[], int from, int n)
{
    if(from == to) return A[from];
    int mid = (from + to) >> 1;
    int m1 = MaxSubArray(A, from, mid);
    int m2 = MaxSubArray(A, mid + 1, to);
    int left = A[mid];
    int now = A[mid];
    for(int i = mid - 1; i >=from; i--)
    {
        now += A[i];
        left = max(left, now);
    }
    int right = A[mid + 1];
    now = A[mid + 1];
    for(int j = mid + 2; j <= to; j++)
    {
        now += A[j];
        right = max(right, now);
    }
    int m3 = left + right;
    int maxSum = max(m1, m2, m3);
    return maxSum;
}

四. sum数组法
分析:
设sum[i] = A[0] +A[1] + …… +A[i]
记S[i, j]为从子数组A[i],…..A[j]的和,则S[i, j] = sum[j] - sum[i-1],
如何求出最大的S[i, j],一个很直观的想法就是再遍历j的时候,我们使得sum[i-1]保持最小,即可得到在j在当前的最小子数组和,另外每遍历一次j,我们就像当前的到的S[i, j]和保留值作比较,并更新maxSum。

复杂度:时间复杂度O(n),空间复杂度O(n)

代码:

int MaxSubArray(int A[], int n)
{
    int sum[n];
    sum[0] = A[0];
    for(int i = 1; i < n; i++)
        sum[i] = sum[i-1] + A[i];
    int maxSum = sum[0];
    int min = 0;
    for(int j = 1; j < n; j++)
    {
        if(sum[j-1] < min) min = sum[j-1];
        if(sum[j] - min > maxSum) maxSum = sum[j] - min;
    }
    return maxSum;
}

五. 动态规划
分析:
上面的算法将时间复杂度降到了O(n),却将空间复杂度升到了O(1),那么能不能讲空间复杂度降到O(1)呢 答案是肯定的。

不妨设all[i]为子数组A[0]…..A[i]的最大和,start[i]为子数组A[0]…..A[i]且包含A[i]的的最大和,那么如何求出all[i]呢,观察可得,all[i] = max{all[i-1], start[i-1] + A[i], A[i]}

复杂度:时间复杂度O(n),空间复杂度O(1)

代码:

int MaxSubArray(int A[], int n)
{
    int all[n];
    int start[n];
    all[0] = start[0] = A[0];
    for(int i = 1; i < n; i++)
    {
        start[i] = max(start[i-1]+ A[i], A[i]);
        all[i] = max(start[i], all[i-1]);
    }
    return all[n-1];
}

似乎空间复杂度没降下来,但其实以上程序中的数组是没必要的。


int MaxSubArray(int A[], int n)
{
    int nall;
    int nstart;
    nall = nstart = A[0];
    for(int i = 1; i < n; i++)
    {
        nstart = max(nstart + A[i], A[i]);
        nall = max(nstart, nall);
    }
    return nall;
}


  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值