问题描述
- 给定一个数组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[low…high]的最大子数组,使用分治技术意味着我们要将子数组划分为两个规模尽量相等的子数组。也就是说,找到子数组的中央位置,比如
m
i
d
mid
mid,然后考虑求解两个子数组
A
[
l
o
w
…
m
i
d
]
A[low…mid]
A[low…mid]和
A
[
m
i
d
+
1
…
h
i
g
h
]
A[mid+1…high]
A[mid+1…high],
A
[
l
o
w
…
h
i
g
h
t
]
A[low…hight]
A[low…hight]的任何连续子数组
A
[
i
…
j
]
A[i…j]
A[i…j]所处的位置必然是一下三种情况之一:
- 1、完全位于子数组 A [ l o w … m i d ] A[low…mid] A[low…mid]中,因此 l o w ≤ i ≤ j ≤ m i d low \leq i \leq j \leq mid low≤i≤j≤mid
- 2、完全位于子数组 A [ m i d + 1 … h i g h ] A[mid+1…high] A[mid+1…high]中,因此 m i d < i ≤ j ≤ h i g h mid < i \leq j \leq high mid<i≤j≤high
- 3、跨越了中点,因此 l o w ≤ i ≤ m i d < j ≤ h i g h low \leq i \leq mid < j \leq high low≤i≤mid<j≤high
- 因此, A [ l o w … h i g h ] A[low…high] A[low…high]的最大子数组所处的位置必然是这三种情况之一。我们可以递归地求解 A [ l o w … m i d ] A[low…mid] A[low…mid]和 A [ m i d + 1 … h i g h ] A[mid+1…high] A[mid+1…high]的最大子数组,因为这两个子问题仍是最大子数组问题,只是规模更小。因此,剩下的全部工作就是寻找跨越中点的最大子数组,然后在三种情况中选取和最大者。如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/93de4563c20e59acac2744aa2709e4ba.png)
- 3、线性查找 O ( n ) \Omicron(n) O(n):此种方法就是顺序查找求和然后与原和对比,若大则换,否则还是用原值。在换sum的时候其左右两边的值各要变换即同步进行。
- 逐个查找过程如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/828ef058586d6a806176e8cface38ab5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a7e5c779f67b9b88a29b97fb15d011cf.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e367a6e7755f6d9b0cf3174c1955552a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7b419ed47fb930e5a1d1dbb566ac2f73.png)
![](https://i-blog.csdnimg.cn/blog_migrate/87595bee9fbe7403db45cba79fe67b5c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bd2eeca7e661d1192680c56e2379b31b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/21ec00806f68e2648a5c3014a5236030.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f6992c27f4c0f3935bb10d2fb0c3e9f1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3e20218aacf2c1a0c7e94c11b8d09c32.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0e51a28e66a81cc2d5064a26d49fb0e0.png)
![](https://i-blog.csdnimg.cn/blog_migrate/53712937249082719491d179d48bbf7a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d3b667bf8a2004c370e6f83427db12bf.png)
![](https://i-blog.csdnimg.cn/blog_migrate/34f85cf11ac9bc51dcba8bc610b9cf98.png)
![](https://i-blog.csdnimg.cn/blog_migrate/99e79e83a828db84571cedea54d4d9de.png)
![](https://i-blog.csdnimg.cn/blog_migrate/226b1344820369fdf9cb248a0752c01c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b214ebeed961090f2aa187fa756f75dd.png)
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;
}
注:本节内容参考算法导论——分治策略