一、思路 (给定n个元素的整数列)
以数组的 center = (right-left)/2 位置处分开。形成两个子数组。
那么,最大子段和 可能出现在三个位置:
1.可能出现在 left子数组 ——>a[1,(n/2)]
2. 可能出现在 right子数组——>a[(n/2)+1,n)]
3.可能出现在 过center的 中间某部分 元素 组成的子数组
p表最左位置
q表最右位置
——>a[p,q]即1=<p=<n/2 , n/2+1=<q=<n
下面考虑 三种情况的计算方法:
第一种情况: 计算 left 到 center 的最大和,记作 left_sum
第二种情况: 计算从 center+1 到 right的最大和,记作 right_sum
第三种情况: 跨边界的和。 以center为中心分别向两边计算和。
1.从 center出发,每次向左边遍历一次,就累加到lefts,如果当前的和比上次的和大,就赋给s1,一直向左直到位置 left。
2.从 center+1出发,每次向右边遍历一次步,就累加到rights,如果当前的值比上次的和 大,就赋给s2,一直向右直到位置right。
3.计算过center的连续值的和,s1+s2的和值 , 这个就是跨边界的和。
上面三种情况考虑计算完成后,最后一步就是,比较三个值中的最大值,取最大值即可。
算法一、算法二和算法四就不过多叙述了直接引用了,其大致内容都差不多
算法一部分代码:T(n)=O(n^2)
算法二部分代码:
算法四部分代码:
图例:
二、算法3代码展示
#include<stdio.h>
#include<stdlib.h>
#define N 100010
//#include<bits/stdc++.h>
//using namespace std;
int max_sum(int a[],int n)
{
return max_sub_sum(a,1,n);
}
int max_sub_sum(int a[],int left,int right)
{
int center,i,left_sum,right_sum,s1,s2,lefts,rights;
if(left==right)
{
if(a[left>0]) return a[left];
else return (0);
}else{
center=(left+right)/2;
left_sum=max_sub_sum(a,left,center);// 左边最大子段
right_sum=max_sub_sum(a,center+1,right);// 右边最大子段
}
//求中间最大子段
s1=0;lefts=0;
for(i=center;i>=left;i--)
{ // 注意:这里是从中间向左遍历,目的是为了让划分的左右两边序列能连接起来
lefts+=a[i];
if(lefts>s1) s1=lefts;
}
s2=0;rights=0;
for(i=center+1;i<=right;i++)
{
rights+=a[i];
if(rights>s2) s2=rights;
}
if(s1+s2<left_sum && right_sum<left_sum) return (left_sum);
if(s1+s2<right_sum) return (right_sum);
return s1+s2;
}
void display(int a[],int n)
{
int i;
for(i=1;i<=n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
}
main(){
/*-2 11 -4 13 -5 -2*/
int n,a[N],i,sum=0;
printf("input:");
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
printf("output:");
display(a,n);
printf("最大子段和为:");
printf("%d",max_sum(a,n));
}
三、运行结果