《编程之美》-求数组的子数组之和的最大值错误

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangfenghui132/article/details/76975177

12年阅读的《编程之美》,那时候的《剑指offer》可能还没有出来,或者刚印刷不久。我的博客原文:http://www.cnblogs.com/2284275841qq/archive/2012/09/17/2689754.html

     我的博客园里面记录了书中的一个错误,好多年没有翻看那本书了,不知道现在那个问题纠正了没有,当时仔细看的时候,貌似发现里面的错误不止一两处,记录下来的现在翻看以前的博客,就只有一个了,可能当时太懒,没全写下来。

     我阅读了《编程之美——微软技术面试心得》一书,无意间发现书中出现了一个算法上的错误。 

其问题在P184,《求数组的子数组之和的最大值》一章中的扩展问题。问题如下:如果数组首尾相邻,也就是我们允许找到一段数字(A[i].....A[n-1],A[0],....A[j])请使其和最大,怎么办? 
其中分为了两种情况,错误处在书中对第二种情况的分析。其中 当i<=j时 则 M_2=A[0]+....+A[n-1],这一步骤是错误的。我举一例数组可以证明 900 -1000 1500 100 -1000 900.此时i<j。若按书上所说M_2=A[0]+...A[5]=1400.然而此时第一种情况返回的最大值为1500+100=1600.由于首尾相连,此时显然 还有个900+900+(-1000)+100+1500=2400.此字串才是和最大的。 
我对他们的算法错误做出了以下分析。如图: 
A————— C————————————B—————————————D 
此为整个字符串,假设AB段为以A[0]为起点的和最大子串,那么BD段之和必为非正。同理CD是以A[n-1]结尾的和最大字串,那么AC段之和必为非正。所以CB段之和必然大于AD(整个字符串)之和。 
以下是我对此题作出的正确分析: 
第一种情况:解没跨过A[n-1]到A[0],原问题已给出解。 
第二种情况:解跨过A[n-1]到A[0],有第一个问题想到不妨把数组看成一条直线。解没跨过时(第一种情况),这个解把数组分成了前中后三段,其中前后段长度的可能为零。中间一段是和最大的子串,数组和是一定的,当首尾相连,那么前后端之和相加必然是和最小的。回到第二种情况看,由于最大字串跨过A[n-1]到A[0],那么最小字串必然不跨过A[n-1]到A[0](情况类似情况一),所以不防用原问题解法求出数组中和最小的字串,由于数组之和不变,所以当你求出最小子串之和时,拿着总数组之和减去最小字串之和,就得到首尾段相加后和最大时得值。由于首尾相连,所以可以得到第二种情况的值。得到正解。 
另外,当第一次看到此题的时候,由于不求位置,我的第一想法就是把数组缩短一下,一个整数数组无非包含正与负,把相连的同号的整数相加,例如数组1 2 3 -4 -5 -6 7 8 -9 -10 可以缩成1+2+3=6 -4-5-6=-15 7+8=15 -9-10=-19 这种情况。 
以下是我写的缩合程序 
int suojian(int *A ,int n) 

int k=0; 
for(int i=1;i<n;i++) 
if(A[i]*A[i-1]<0) 
{k++;A[k]=A[i];} 
else A[k]=A[k]+A[i]; 
return k; 

如果对于较大的数组,对于拓展后的问题,应该可以节省下来不少计算。 
刚接触计算机,涉猎粗浅,不知道上面写的有错误没有,我只是提出了自己的看法,如果上面的有错误还望海涵

猜你喜欢

转载自blog.csdn.net/wangfenghui132/article/details/76975177