来源于《编程珠玑》第八章的介绍:求一个向量的元素和最大的连续子向量,使用扫描算法可以将计算量化简到O(n)量级。
问:向量X = { -2, 7, 1, -6, -2, 9, 2, -1 }(n=8) , 求它的和最大的连续子向量。
先看求解过程:
X { -2, 7, 1, -6, -2, 9, 2, -1 }
max_end { 0, 7, 8, 2, 0, 9, 11,10 } max_end = max( max_end + X(i), 0)
max_sofar { 0, 7, 8, 8, 8, 9, 11,11 } max_sofar = max(max_end, max_sofar);
解释一下:
max_end 类似于积分值,但又有不同。
X { -2, 7, 1, -6, -2, 9, 2, -1 }
max_end { 0, 7, 8, 2, 0, 9,11,10 }
integration{ -2, 5, 6, 0, -2, 7, 9, 8}
max_end 有较强的物理意义:max_end 期望尝试向右累加以获得最大子向量。
如果X全是正数,max_end 向右累加持续增大;
如果X全是负数,max_end 将一直为0;
如果X有正有负,假设max_end 现在为正,max_end 向右累加,当加到小于0时,有一点可以肯定X(i)<0, 发现没有必要再向右加了,把当前max_end 置0,继续扫描。
从左往右扫描,如果第一个数是负的,抛弃(max_end = 0);
第二个数是正的,开始累加,和max_sofar比较并将最大值记录在max_sofar里;
第三个数,累加后值还是正的,和max_sofar比较并将最大值记录在max_sofar里;
第四个数,累加后值还是正的,和max_sofar比较并将最大值记录在max_sofar里;
第五个数,累加后值是0,说明累加值已经递减到0,可得二、三、四、五属于一个山峰,其中二、三是局部最大子
向量。此时max_sofar = 8,记录了局部山顶的值8,并且是在第三个数的时候到达。
第六个数是正的,开始新的累加,和max_sofar比较并将最大值记录在max_sofar里;
第七个数,累加后值还是正的,和max_sofar比较并将最大值记录在max_sofar里;
第八个数,累加后值还是正的,和max_sofar比较并将最大值记录在max_sofar里;此时max_sofar = 11,记录
了局部山顶的值11,并且是在第七个数的时候到达。
可见,这个方法扫描出一座座山峰(不能合并的局部最大和子向量),最后看看哪个子向量的海拔高(和最大)。max_end中的0指示了山峰的山脚(一座山的开始和结束)。这个算法最核心的地方是何时将max_end 置0。光是积分显然做不到,
max_end = max( max_end + X(i), 0)这句代码提供了何时以及怎么处理负数的核心问题。