解法一:
穷举的尝试所有的可能
int sovleone(const int a[], int n)
{
int thissum, sum, i, j, k;
sum = 0;
for (i = 0; i < n; i++)
{
for (j = i; j < n; j++)
{
thissum = 0;
for (k = i; k <= j; k++)
{
thissum += a[k];
}
if (thissum > sum)
sum = thissum;
}
}
return sum;
}
这个是书上的代码,类似便利的方法大同小异
int nextsolveone(const int a[], int n)
{
int thissum, sum, i, j, k;
sum = 0;
for (i = 0; i < n; i++)
//每一项作为起点都要便利
{
for (j = 0; j + i < n; j++)
//每一项之后J-1个数字之和,(j=0则就这一个数字)
{
thissum = 0;
/* 5 */ for (k = i; k - i <= j&&k < n; k++)
//这里可能要留心,是k-i<j还是k-i<=j
{
/* 6 */ thissum += a[k];
}
if (thissum > sum)
sum = thissum;
}
}
return sum;
}
这两个代码相比,感觉书上的明显要整齐一点。
复杂度:O N^3
注意在第五行和第六行中(看注释),计算机会重复计算加法运算,这种方式是不好的。
比如 i=0; j = 1 和 j = 2
在 j = 1的时候 计算机计算了 thissum += a[0]
在 j = 2的时候 计算机计算了 thissum += a[0] thissum += a[1]
显然 thissum+=a[0]被重复计算了,,这是很不友好的行为。所以解法二的优化从此而来。
解法二:
int solvetwo(const int a[], int n)
{
int thissum, sum, i, j;
sum = 0;
for (i = 0; i < n; i++)
{
thissum = 0;
for (j = i; j < n; j++)
{
thissum += a[j];
if (thissum > sum)
sum = thissum;
}
}
return sum;
}
这样简单的优化就使得复杂度变为 O N^2
解法三:递归 复杂度为 N*log N
思路:最大子序列可能在三处出现,或者整个出现在数组的左半部分,或者整个出现在数组的右半部分,或者跨越数组中间从而占据左右两个部分。前两种情况可以进行递归。递归的出口条件应该是只剩余一个元素,如果该元素大于0,则该元素就为最长序列,否则返回0;
int solvethreefordigui(const int a[], int left, int right)
{
int maxleftsum, maxrightsum;
int maxleftbordersum, maxrightbordersum;
int leftbordersum, rightbordersum;
int center, i;
if (left == right)
//递归的出口条件,如果只有一个元素,该元素非负时它就是最大子序列
{
if (a[left] > 0)
return a[left];
else
return 0;
}
center = (left + right) / 2;
maxleftsum = solvethree(a, left, center);
//在左半部分的最大值,通过递归求解。
maxrightsum = solvethree(a, center+1, right);
//右半部分的最大值。
//中间部分的最大值,要求向左/右的第一位必须被包含,之后连起来。
maxleftbordersum = 0;
leftbordersum = 0;
for (i = center; i >= left; i--)
{
leftbordersum += a[i];
if (leftbordersum > maxleftbordersum)
maxleftbordersum = leftbordersum;
}
maxrightbordersum = 0;
rightbordersum = 0;
for (i = center + 1; i <= right; i++)
{
rightbordersum += a[i];
if (rightbordersum > maxleftbordersum)
maxrightbordersum = rightbordersum;
}
//返回左/右,中间部分的最大值。
return max3(maxleftsum, maxrightsum, maxleftbordersum + maxrightbordersum);
}
int solvethree(const int a[], int n)
{
return solvethreefordigui(a, 0, n - 1);
}
解法四:
似乎要放大招了
int solvefour(const int a[], int n)
{
int thissum, maxsum, j;
thissum = maxsum = 0;
for (j = 0; j < n; j++)
{
thissum += a[j];
if (thissum > maxsum)
maxsum = thissum;
else if (thissum < 0)
thissum = 0;
}
return maxsum;
}
很厉害,复杂度为完全的O (N)
它只对数据进行一次扫描,一旦完成对a[j]的读入和处理,就不再需要记忆它了。并且,在读入的任何时刻,它已经对以读入的数据得出了正确的答案(假设读入前i个,则此时的maxsum就是前i个中的最长序列)。(这种算法叫联机算法。
对算法的理解:我们从最长子序列的起点的确定来考虑;
情况如下 : thissum a[j+1] a[j+2]
如果 thissum < 0 那么最长子序列的开始只能在 a[j+1]或更往后。所以应该有 thissum = 0
(或者可以理解为:假设a[j+1]为一个正数,那么如果thissum大于0,则两个正数相加变更大,
如果thissum <0 那么 a[j+1]就不需要加上前面的thissum。加上只会变小。
对这种比较秀的算法,自己只能手动跑几个案例,来证明他是对的,目前感觉还是要多看几遍来有一点点印象吧,之后碰见才会用到这个高效的算法。。。想一瞬间就想到感觉还是比较难的。。。
留一个刚刚碰见的格言:
计算任何事情不要超过一次。
新年快乐。