剑指offer:查找最大子数组(C语言)

博客介绍了如何在整数数组中找出连续子数组的最大和。当数组全为正数时,直接累加;全为负数时,选择最大元素;有正负时,通过四种解法求解,包括暴力求解、一次遍历、分治法和动态规划。解法二仅遍历一次,解法三时间复杂度为O(nlogn),解法四是动态规划的优化版本。

题目:一个整数数组中的元素有正有负,在该数组中找出一个连续的数组,要求连续的数组的和是最大的,我们成这个子数组就是最大的连续子数组。

分析:对于这个找最大子数组的数组,如果这个数组里面全部是正数,那就不用考虑了,所有元素相加就是最大的子数组了;如果全部为负,则就是找里面的最大的元素;如果有正有负,则情况会麻烦一下。下面提供四种解法。

解法一:

int FindSubSum1(int *arr,int length)
{
int maxSum = 0;
for(int i = 0; i < length; i++)
{
    int curMax = 0;
    for(int j = i; j < length; j++)
    {
         curSum += arr[i];
         if(curSum > maxSum)
             maxSum = curSum;     
    }
}
return maxSum;
}

解法看起来就非常的简单粗暴,两轮循环,先从第一个数开始往后累加,每次累加之后都和最大的和比较,找到最大的那个累加和;然后从第二个往后累加,以此类推。该算法的时间复杂度为O(n^2)。

 

解法二:

int FindSubSum2(int *arr,int length)
{
    int maxSum = 0, curMax = 0;
    for(int i = 0; i < length; i++)
    {
        curSum += arr[i];
        if(curSum > maxSum)
            maxSum = curSum;
        if(curSum < 0)
            curSum = 0;     
        }
    }
    return maxSum;
}

该解法采用一种特别的方式,只遍历数组一遍,当然,这种方法要求数组中必须要有非负数存在。我遍历的时候遇到和小于0的,肯定就不是最大的那个数组了,这个非常容易想明白。

解法三:

int max(int a,int b,int c)
{
    int max = (b > a) ? b : a;
    max = (max > c) ? max :c;
    return max;
}

int FindSubMax3(int *arr,int left,int right)
{
    int maxLeftSum,maxRightSum;   //左右的最大的和
    int maxLeftBorderSum,maxRightBorderSum;  //含中间边界的左右部分最大和
    int leftBorderSum,rightBorderSum;    //含中间边界的左右部分当前和

    if(left == right)
        return arr[left];

    //求含中间边界的左右部分的最大值
    int center = (left + right) / 2;
    maxLeftBorderSum = 0;
    leftBorderSum = 0;
    for(int i = center; i >= left; i--)
    {
        leftBorderSum += arr[i];
        if(leftBorderSum > maxLeftBorderSum)
            maxLeftBorderSum = leftBorderSum;
    }
    maxRightBorderSum = 0;
    rightBorderSum = 0;
    for(int i = center + 1; i <= right; i++)
    {
        rightBorderSum += arr[i];
        if(rightBorderSum > maxRightBorderSum)
            maxRightBorderSum = rightBorderSum;
    }
    //递归求左右部分的最大值
    maxLeftSum = FindSubMax3(arr, left, center);
    maxRightSum = FindSubMax3(arr, center + 1, right);

    return max(maxLeftSum,maxRightSum,maxLeftBorderSum + maxLeftBorderSum);
}

这种解法就是求解分为三部分,以位于中间的那个值为界,分别对其左边、右边、跨过中间值这三部分求值,然后比较它们的大小,其中左边和右边的比较好理解,跨过中间的就需要两部分来拼接了,也就是最后的maxLeftBorderSum + maxLeftBorderSum。这个的时间复杂度为O(nlogn)。

解法四:

int FindSubMax4(int *arr,int length)
{
int dp[length];
dp[0] = arr[0];
int maxSum = dp[0];
int temp = 0;

for(int i = 1; i < length; i++)
{
     if(dp[i - 1] <= 0)
     {
         dp[i] = array[i];
     }
     else
         dp[i] = arr[i] + dp[i - 1];
     if(dp[i] > maxSum)
    {
        maxSum = dp[i];
    }
}
return maxSum;
}

动态规划的方法,初始化一个最大数组dp[length],这个数字表示arr[m]...arr[n]的数组的和,m会随着dp[i - 1] <= 0这句判断语句而变化,最终得到最大值maxSum。它的简化版就是解法二了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值