对<编程之美>中的寻找发帖"水王"的理解

Posted on 2011-10-22 19:22  EmPower  阅读(207)  评论(0)    收藏  举报

    呵呵,博客园的hello world文章.今天在学习编程之美书上的题,遇到了这个题.具体的题目我就不来讲了,博客园里有很多这道题的说明.我想侧重讲一下对书中提出的解法的理解.

    这道题要解决的问题,可以概括为:在无序且存在重复的序列中,找出重复最多的那个元素.前提:重复最多的元素超过总数的一半.

    按照传统的解法,当然是先排序,然后再扫描一遍,记录每个元素的出现次数.这样的话,正如书中所说,复杂度为O(N*logN + N).针对这道题,考虑到已知重复最多的元素的数量是超过总数的一半的,那么我们可以不需要排序,而是每次删除两个不同的元素,那么在剩下的序列中,重复最多的那个元素还是依然超过总数的一半.这可以证明之.

    求证: N个含有重复的整数中,其中重复次数最多的整数ID,其数量为K,且 k>N/2.证明当删除两个不同的整数后,重复最多的元素依旧是该元素,且数量依然超总数一半.

    证明: 情况1,删除的两个整数都ID,那么显然ID在新序列中所占比值更大了.

     情况2,删除的两个整数中,其中有一个是ID,那么ID的新比值为(K-1)/(N-2). 那么K/N - (K-1)/(N-2) = (N-2K)/N(N-2), 由于K>N/2,可知K/N < (K-1)/(N-2), 即ID的比值更大了.

    于是得证.

    根据这个结论,我们只需要设计一段程序,遍历序列的过程中不停地将不同的元素删除即可.但是书中的代码似在我的设计水平之上,一开始不能理解是如何运用上面的结论的.他的代码是这样的:

View Code
 1 int Find(int *ID, int n)
2 {
3 int candidate;
4 int nTimes,i;
5 for(i=nTimes=0; i<n; ++i)
6 {
7 if(nTimes==0)
8 {
9 candidate = ID[i];
10 nTimes=1;
11 }
12 else
13 {
14 if(candidate == ID[i])
15 {
16 nTimes++;
17 }
18 else
19 {
20 --nTimes;
21 }
22 }
23 }
24 return candidate;
25 }


    花了一段时间,我终于理清了,下面给出我的理解:

  1. Find函数从0->N遍历整个ID序列;candidate最终将指向重复最多的ID;nTimes记录所指candidate的重复次数,初始为0.

  2. 开始遍历,首先将candidate初始为ID[0],nTimes设为1;

  3. 遍历下一个,如果当前nTimes为0(表示目前已经没有与candidate相同的ID可用了),则将candidate指向当前ID(相当于删除了之前的candidate),nTimes=1;否则4;

  4. 如果candidate所指与当前ID相同,那么nTimes加1(相当于找到"替死鬼");否则减1(按理来说是要candidate删除并前移的,但是这里相当于杀了"替死鬼",等到nTimes=0时,直接跳到此时所指的ID);

  5. 重复3, 直到最后.返回candidate.

  这段代码是具有特殊性的,即有前提条件.如果最多重复的ID没有超过一半,则行不通了.但对于书中的扩展,条件是已知有三个重复很多的ID,且都超过了总帖数的1/4.这可以用类似的方法,即每次减去4个不相同的元素.具体的也就是用nTimes[3]分别统计每个待找ID的"替死鬼"个数.代码结构和上述类似.

  Ok,从今天起坚持每天至少写一篇关于算法的解题思路,不管大小,重在积累.相比园中老鸟,我貌似为时已晚.但又以哈佛那句训言自勉:觉得为时已晚的时候,恰恰是最早的时候.对于这份一辈子的事业,为时不晚.



博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3