编程珠玑读后感1(1个算法)

在《编程珠玑》第八章,作者提出一道算法题:

程序描述:
输入是一个具有N个浮点数字的向量X: 其输出是在输入的任何相邻子向量中找出的最大和。
例如,输入如下10个元素:
31 -41 59 26 -53 58 97 -93 -23 84
返回 x[2..6]的总和,或为187

作者分别给出了三次算法O(n^3),两个二次算法O(n^2),分治算法O(n logn )和扫描算法O(n)。

我对该问题作了研究后,给出自己的一种解法。

考虑数组 x[ N ],我们新开辟一个数组sumX[ N + 1 ],其中sumX[ 0 ] = 0, 而 sumX[ i ] 为 x[ 0..i-1 ] 的和。
这样,对于 x[ i..j ]的和,有 sum( x[ i..j ] ) = sumX[ j+1 ] - sumX[ i ].
对于数组sumX, 假设最大值为 sumX[ posMax ], 最小值为 sumX[ posMin ],下面分三种情况:

1. posMin < posMax,也即最小值在最大值左边,我们可以肯定 x[ posMin..posMax-1 ] 就是所要求的总和。
否则,若x[ i..j ]是所要求的总和,且 i != posMin或 j != posMax,
则有sum( x[ i..j ] ) <= sum( x[ posMin, j ] ) <= sum( x[ posMin, posMax ] )
或sum( x[ i..j ] ) <= sum( x[ i, posMax ] ) <= sum( x[ posMin, posMax ] )
上面两不等式至少有一个严格小于,所以sum( x[ i..j ] ) < sum( x[ posMin, posMax ] )

由上可知,这种情况中返回结果 sumX[ posMax ] - sumX[ posMin ]

2. posMin == posMax,说明除了 x[ posMin ]外,其余x[ i ] 全为0
又由于sumX[ 0 ] = 0,所以 sumX[ posMax ] > 0
所以 x[ posMax - 1 ]是所要求的总和

这种情况中返回结果 sumX[ posMax ],也就是 x[ posMax-1 ] 一个元素

3.posMin > posMax
这种情况比较复杂,我们可以把sumX分为三部分:
a)sumX[ 0 ]..sumX[ posMax ];  b)sumX[ posMax + 1 ] ... sumX[ posMin - 1 ], c)sumX[ posMin ].. sumX[ N + 1 ].
我们可以肯定,要找出的x[ i..j-1 ],也可以认为是sumX[ i],sumX[j ]必在同一个小部分中。
否则,若 0 <= i <= posMax 而 posMax < j
则sum( x[ i..j-1 ] ) = sumX[ j ] - sumX[ i ] < sumX[ posMax ] - sumX[ i ]。
对于其他情况,也可以同样证明。

于是我们可以分别对数组a,b,c处理,找出最大和。
对数组a,找出最小值sumX[ posMin1 ],必有posMin1 < posMax,
于是结果 tmpMax1 = sumX[ posMax ] - sumX[ posMin1 ]

对数组c,找出最大值sumX[ posMax3 ],必有posMin < posMax3,
于是结果 tmpMax3 = sumX[ posMax3 ] - sumX[ posMin ]

对数组b,这是一个比开始问题规模更小的子问题,可再迭代处理,得到结果tmpMax2

这种情况中返回结果 tmpMax1, tmpMax2, tmpMax3中最大值


这种算法在最优情况下(posMin <= posMax)可看作一线性算法,运行时间O(n)
在最坏情况下(每次处理均有posMax = 0, posMin = 数组索引最大值,每个子问题规模减2),
这时可看作二次算法,运行时间O(n^2)
使用随机数调试,发现大部分情况运行时间在O( n logn ) 与 O( n^2 )之间。

测试结果:
N=10  N*logN=23  N*sqrtN=31  N^2=100  程序运行次数:27
N=100  N*logN=460  N*sqrtN=1000  N^2=10000  程序运行次数:404
N=1000  N*logN=6907  N*sqrtN=31622  N^2=1000000  程序运行次数:13009
N=10000  N*logN=92103  N*sqrtN=1000000  N^2=100000000  程序运行次数:683867 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值