雁过无痕

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::

《编程之美》读书笔记102.18 数组分割

如果直接遍历,则至少要遍历 Cr(n-1,2*n)次(Cr(m,n)为从n个数中取m个数的组合数),为了减少遍历次数,可以先对数组排序。再将所有可能的组合大致分成几组,每个组的数组和也是升序的,通过不断的分组、查找,确定上下边界条件,最终找到所求子数组。

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

先将数组{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<=STn+1即为所求,如果存在Ti=S,则Ti即为所求。否则,可以通过二分法找到唯一的ij使得Ti<S<Tj,(选中a0aj),记录Ti,假设Ri=sum(a0+aj+ai+an+3+an+4…+a2n-1) (j<i<=n+2),则Tj=Rn+2,比较TiRj+1Rn+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进行查找,如果S02670367之间。只要记录0267,并在适当时候判断该记录是否是所求的,展开0345——03676个数,

0345 —— 0347(共3个)

0356 —— 0357(共2个)

0367 —— 0367(共1个)

再重复上述操作。

 

posted on 2010-08-16 00:16 flyinghearts 阅读(1143) 评论(0)  编辑 收藏 引用 所属分类: 编程之美

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理