flyinghearts《编程之美》读书笔记连载(12)

摘自:http://blog.csdn.net/flyinghearts/archive/2010/05/18/5605927.aspx

编程之美读书笔记_2.18 数组分割

为了减少遍历次数,可以先对数组排序。再将所有可能的组合大致分成几组,每个组的数组和也是升序的,通过不断的分组、查找,确定上下边界条件,最终找到所求子数组。

如果数组各个元素均不相同,可以采用下面的算法:

先将数组 {ai } 排序,并计算出各元素的总和的一半 S(=Sum/2.0) ,(对数组的划分时,可以先选中 a0 ,再取 n-1 个数)。

假设 Ti =sum(a0 +ai +an+2 +an+3 …+a2n-1 ) (0<i<=n+1) 则数组 {Ti } 也是升序。如果 Tn+1 <=S 则 Tn+1 即为所求,如果存在 Ti =S ,则 Ti 即为所求。否则,可以通过二分法找到唯一的 i 、 j 使得 Ti <S<Tj ,(选中 a0 和 aj ),记录 Ti ,假设 Ri =sum(a0 +aj +ai +an+3 +an+4 …+a2n-1 ) (j<i<=n+2) ,则 Tj =Rn+2 ,比较 Ti 、 Rj+1 、 Rn+2 ,再对 Ri 进行类似 Ti 的分析,找出下一个数。通过不断的分组和查找和判断,最终可以找到所求的 n 个数。

例如:长度为 8 的数组:共有 35 种组合,对每种组合的子数组和,可以划分到几个区间: ( 下面的 0123 表示取 a0 +a1 +a2 +a3 )

较小值    较大值

0123 —— 0167 (共 15 个)

0234 —— 0267 (共 10 个)

0345 —— 0367 (共 6 个)

0456 —— 0467 (共 3 个)

0567 —— 0567 (共 1 个)

( 各个较大值不必计算,它们间必然只有一个数不同(并且这个不同的数在升序数列中的位置是连续的),查找 S 在哪两个较大值之间,可以用 S 减去相同的数的和,得到的差去指定的范围(不同的那个数的位置范围)进行二分查找。 )

由于数组是升序,数组元素各不相同,右边的“较大值”都是升序排列且不会重复。利用数组和的一半 S 进行查找,如果 S 在 0267 和 0367 之间。只要记录 0267 ,并在适当时候判断该记录是否是所求的,展开 0345 —— 0367 的 6 个数,

0345 —— 0347 (共 3 个)

0356 —— 0357 (共 2 个)

0367 —— 0367 (共 1 个)

再重复上述操作。

 

延伸阅读:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值