您的位置:首页 > 其它

最大子数组问题

2013-12-12 00:34 106 查看
最大子数组问题就是求数组中和最大的非空连续子数组。比如13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7的最大子数组就是18, 20, -7, 12。下面分别用三种方法来求解此问题。
定义全局最大子数组结构体:
//最大子数组结构
struct  MaxSubArray
{
int max_left; //最大子数组左边界
int max_right;//最大子数组右边界
int max_sum;//最大子数组和
MaxSubArray(int maxLeft, int maxRight, int maxSum)
:max_left(maxLeft), max_right(maxRight), max_sum(maxSum){}
};

方法一:暴力求解,时间复杂度O(n2)

此方法是求出每一个子数组的和,然后取最大值,从而得出结果。

代码如下:

暴力解法,求出每个子数组的和,取最大值
MaxSubArray findMaxSubarray(int A[], int n)
{
int max_left = 0, max_right = 0, max_sum = A[0];
int sum = 0;
for (int i=0; i<n; i++)//每个i开始的子数组
{
sum = 0; //保存子数组A[i:j-1]的和;
for (int j=i; j<n; j++)//产生A[i:j]的子数组
{
if ((sum += A[j]) > max_sum)  //更新
{
max_sum = sum;
max_left = i;
max_right = j;
}
}
}
return MaxSubArray(max_left, max_right, max_sum);
}

方法二:分治求解,时间复杂度O(nlgn)

我们可以这样理解此问题,在数组A[low:mid:high]中,最大子数组要么存在于A[low:mid],要么存在于A[mid+1:high],要么跨越mid存在于A[i:mid:j],其中(low<=i<mid, mid<j<=high)。因此,只要取以上三种情况的最大值即可得出答案。

代码如下:

//求跨越数组A[low:high]中间位置mid的最大子数组
MaxSubArray findMaxCrossSubArray(int A[], int low, int mid, int high)
{
int max_left=mid, left_sum = A[mid];
int sum = 0;
for (int i=mid; i>=low; i--)
{
if((sum+=A[i]) > left_sum)
{
left_sum = sum;
max_left = i;
}
}

int max_right = mid+1, right_sum = A[mid+1];
sum = 0;
for (int j=mid+1; j<=high; j++)
{
if ((sum += A[j]) > right_sum)
{
right_sum = sum;
max_right = j;
}
}
return MaxSubArray(max_left, max_right, left_sum+right_sum);
}

//分治法求A[low:high]的最大子数组
MaxSubArray findMaxSubarray(int A[], int low, int high)
{
if(low>=high) //少于一个元素
return MaxSubArray(low, low, A[low]);
else
{
int mid = (high+low)/2;
MaxSubArray left = findMaxSubarray(A, low, mid); //求左边的最大子数组
MaxSubArray right = findMaxSubarray(A, mid+1, high); //求右边的最大子数组
MaxSubArray cross = findMaxCrossSubArray(A, low, mid, high);//求跨越mid的最大子数组
if(left.max_sum >= right.max_sum && left.max_sum >= cross.max_sum)
return left;
else if(right.max_sum >= left.max_sum && right.max_sum >= cross.max_sum)
return right;
else
return cross;
}
}

方法三:顺序扫描,时间复杂度O(n)

此方法的原理就是:从数组的左边界开始,从左至右记录到目前为止已经处理的最大子数组;已知A[1:j]的最大子数组,那么A[1:j+1]的最大子数组要么就是A[1:j]的最大子数组,要么就是A[i:j+1](1=<i<=j+1)。具体实现如下:

在数组A[1:n]中,初始时,设最大子数组的左边界max_left右边界max_right都为1,和max_sum=A[1];

sum累加i(初始为1)到j+1的和(1=<i<=n),如果sum小于0,则新的最大子数组的新左边界应从j+2开始,i置j+2,sum置为A[j+2];

如果sum大于A[1:j]的最大子数组的和max_sum,则更新最大子数组的边界,即max_left=i,max_right=j+1,同时更新max_sum=sum。

代码如下:

MaxSubArray findMaxSubarray(int A[], int n)
{
int max_left = 0, max_right = 0, max_sum = A[0];
int i = 0, sum = 0;
for (int j=0; j<n; j++)
{
if (sum < 0)
{
sum = A[i];//A[max_left:i-1]之和小于0,不应再考虑,重新从i开始
i = j;
}
else
sum += A[j];
if(max_sum < sum) //新的和,更新右边界
{
max_left = i;
max_right = j;
max_sum = sum;
}
}
return MaxSubArray(max_left, max_right, max_sum);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: