分治——求最大子数组

问题描述
  • 给定一个数组array[],数组元素均为整数(有正有负),求取其中一部分字串,使其和最大,输出最大的和与该字串的在原数组的头尾位置。
问题分析
  • 1、暴力求解:在数组长度为 n n n的数组中两两组合共有 C n 2 C^2_n Cn2种,因为 C n 2 = Ω ( n 2 ) C^2_n = \Omega(n^2) Cn2=Ω(n2),因此这种方法的时间复杂度为 O ( n 2 ) \Omicron(n^2) O(n2).
  • 2、分治法 O ( n l o g n ) \Omicron(nlogn) O(nlogn):假设我们要寻找子数组 A [ l o w … h i g h ] A[low…high] A[lowhigh]的最大子数组,使用分治技术意味着我们要将子数组划分为两个规模尽量相等的子数组。也就是说,找到子数组的中央位置,比如 m i d mid mid,然后考虑求解两个子数组 A [ l o w … m i d ] A[low…mid] A[lowmid] A [ m i d + 1 … h i g h ] A[mid+1…high] A[mid+1high] A [ l o w … h i g h t ] A[low…hight] A[lowhight]的任何连续子数组 A [ i … j ] A[i…j] A[ij]所处的位置必然是一下三种情况之一:
    • 1、完全位于子数组 A [ l o w … m i d ] A[low…mid] A[lowmid]中,因此 l o w ≤ i ≤ j ≤ m i d low \leq i \leq j \leq mid lowijmid
    • 2、完全位于子数组 A [ m i d + 1 … h i g h ] A[mid+1…high] A[mid+1high]中,因此 m i d < i ≤ j ≤ h i g h mid < i \leq j \leq high mid<ijhigh
    • 3、跨越了中点,因此 l o w ≤ i ≤ m i d < j ≤ h i g h low \leq i \leq mid < j \leq high lowimid<jhigh
  • 因此, A [ l o w … h i g h ] A[low…high] A[lowhigh]的最大子数组所处的位置必然是这三种情况之一。我们可以递归地求解 A [ l o w … m i d ] A[low…mid] A[lowmid] A [ m i d + 1 … h i g h ] A[mid+1…high] A[mid+1high]的最大子数组,因为这两个子问题仍是最大子数组问题,只是规模更小。因此,剩下的全部工作就是寻找跨越中点的最大子数组,然后在三种情况中选取和最大者。如下图所示:
  • 3、线性查找 O ( n ) \Omicron(n) O(n):此种方法就是顺序查找求和然后与原和对比,若大则换,否则还是用原值。在换sum的时候其左右两边的值各要变换即同步进行。
  • 逐个查找过程如下图所示:
C语言代码示例
  • 1、分治法
#include<stdio.h>

void FindMaxCrossingSubArray(int a[], int low, int mid, int high, int result[])
{
  int leftSum = 0;
  int sum = 0;
  int rightSum = 0;
  int maxLeft = 0;
  int maxRight = 0;
  int i = mid;
  for( ; i >= low; i--)
  {
    sum += a[i];
    if(sum > leftSum)
	{
      leftSum = sum;
      maxLeft = i;
	}
  }
  sum = 0;
  for(i = mid+1; i <= high; i++)
  {
    sum += a[i];
    if(sum > rightSum)
    {
      rightSum = sum;
      maxRight = i;
    }
  }
  result[0] = maxLeft;
  result[1] = maxRight;
  result[2] = leftSum + rightSum;
}

void FindMaximumSubArray(int a[], int low, int high, int result[])
{
  int mid = 0;
  int leftResult[3], rightResult[3], crossResult[3];
  if(high == low)
  {
    result[0] = low;
    result[1] = high;
	result[2] = a[low];
    return;
  }else{
    mid = low + (high - low)/2;
    FindMaximumSubArray(a, low, mid, leftResult);
    FindMaximumSubArray(a, mid+1, high, rightResult);
    FindMaxCrossingSubArray(a, low, mid, high, crossResult);
    if(leftResult[2] >= rightResult[2] && leftResult[2] >= crossResult[2])
    {
       result[0] = leftResult[0];
       result[1] = leftResult[1];
       result[2] = leftResult[2];
    }else if(rightResult[2] >= leftResult[2] && rightResult[2]>= crossResult[2])
    {
       result[0] = rightResult[0];
       result[1] = rightResult[1];
       result[2] = rightResult[2];
    }else{
       result[0] = crossResult[0];
       result[1] = crossResult[1];
       result[2] = crossResult[2];
    }
  }
}

int main(){
  int a[16] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
  int result[3];
  FindMaximumSubArray(a, 0, 15, result);
  printf("sum:%d, left:%d, right:%d\n", result[2], result[0], result[1]);
  return 0;
}

  • 2、线性查找
#include<stdio.h>

void Found(int a[], int length, int result[])
{
  int point = 1;
  int sum = 0;
  int tempSum1 = 0;
  int tempSum2 = 0;
  int maxLeft = 0;
  int maxRight = 0;
  int tempSum2Left = 0;
  sum = a[0];
  for( ; point < length; point++)
  {
    tempSum2 += a[point];
    if(tempSum2 < 0)
    {
      tempSum1 += tempSum2;
      tempSum2 = 0;
      tempSum2Left = point+1;
	  continue;
    }else if(tempSum2 >= sum)
    {
      if(sum + tempSum1 >= 0)
      {
        maxRight = point;
        sum += (tempSum1 + tempSum2);
        tempSum1 = 0;
        tempSum2 = 0;
	  }else{
        maxLeft = tempSum2Left;
        maxRight = point;
        sum = tempSum2;
        tempSum1 = 0;
        tempSum2 = 0;
      }
      continue;
    }else if(tempSum1 + tempSum2 >= 0)
    {
      maxRight = point;
      sum += (tempSum1 + tempSum2);
      tempSum1 = 0;
      tempSum2 = 0;
    }
  }
  result[0] = maxLeft;
  result[1] = maxRight;
  result[3] = sum;
}

int main(){
  int result[3];
  int array[16] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
  Found(array, 16 , result);
  printf("result:sum=%d,left=%d,right=%d\n", result[3], result[0], result[1]);
  return 0;
}

注:本节内容参考算法导论——分治策略

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值