题目:
输入一个整型数组,数据元素有正数也有负数,求元素组合成连续子数组之和最大的子数组,要求时间复杂度为O(n)。
例如:
输入的数组为1, -2, 3, 10, -4, 7, 2, -5,最大和的连续子数组为3, 10, -4, 7, 2,其最大和为18。
背景:
本题最初为2005年浙江大学计算机系考研题的最后一道程序设计题,在2006年里包括google在内的很多知名公司都把本题当作面试题。
由于本题在网络中广为流传,本题也顺利成为2006年程序员面试题中经典中的经典。
int MaxSub(int a[],int n,int& begin,int& end)//a[]是给定的数组,n是数组中元素个数,begin和end分别记录起始项和末项
{
int sum=0;//最大子段和
int b=0;//最后一项为a[i]的序列构成的最大子段和
for(int i=0;i<n;i++)
{
if(b>0)
{
b=b+a[i];
}
else
{
b=a[i];
begin=i;//记录起始项
}
if(b>sum)
{
sum=b;
end=i;//记录末项
}
}
if(end==0) { 表示所有数都为负数;这时要找出数组中最大的数; }
return sum;
}
程序结果:输入数组{-1,3,6,-9,2,-5,-1,9,3,-3}得到最大和子序列{9,3}
/// <summary>
/// 获取连续子数组的最大和
/// </summary>
/// <param name="array">目标数组</param>
/// <param name="length">数组的长度</param>
private static void GetMaxSum(int[] array, int length)
{
int sum = 0; //记录当前连续子数组的最大和
int temp = 0; //记录当前数组连续几个元素的和(当其值小于0时,重新对其赋值;【即:抛弃前面的所有元素】)
int startIndex = 0; //记录子数组(和最大)的起始位置
int endIndex = 0; //记录子数组(和最大)的终止位置
int newStartIndex = 0; //记录子数组(和最大)的新的起始位置
for (int i = 0; i < length; i++) //遍历整个目标数组
{
if (temp < 0) //如果temp < 0;则要对temp重新赋值
{
temp = array[i]; //对temp重新赋值
newStartIndex = i; //暂时记录子数组(和最大)的新的起始位置(要看后续的sum 和 temp是否发生交换)
}
else
{
temp += array[i]; //如果temp >= 0;则要继续将此时的数组元素(array[i])加入到temp中
}
if (sum < temp) //如果此时 sum < temp;则表示此时的子数组和大于之前的子数组和
{
sum = temp; //将大的子数组和temp赋值给sum
startIndex = newStartIndex; //子数组(和最大)的新的起始位置起作用了
endIndex = i; //子数组(和最大)的终止位置(只要发生交换就说明endIndex发生变化)
}
}
//显示最终的结果(从数组的第N个元素到M个元素之和最大)
DisplayResult(array, sum, startIndex, endIndex);
}
讨论:上述代码中有两点值得和大家讨论一下:
· 函数的返回值不是子数组和的最大值,而是一个判断输入是否有效的标志。如果函数返回值的是子数组和的最大值,那么当输入一个空指针是应该返回什么呢?返回0?那这个函数的用户怎么区分输入无效和子数组和的最大值刚好是0这两中情况呢?基于这个考虑,本人认为把子数组和的最大值以引用的方式放到参数列表中,同时让函数返回一个函数是否正常执行的标志。
· 输入有一类特殊情况需要特殊处理。当输入数组中所有整数都是负数时,子数组和的最大值就是数组中的最大元素。
本文已经收录到《剑指Offer——名企面试官精讲典型编程题》一书中,有改动,书中的分析讲解更加详细,讨论了这种方法和动态规划方法的联系。欢迎关注。在我的英文博客里也有这道题的讲解,