查找出现次数大于n/k的重复元素
本文是对一篇英文论文的总结:Finding Repeated Elements。想看原文,请Google之。
这个问题的简单形式是“查找出现次数大于n/2的重复元素”。我们先从简单问题开始,然后再做扩展。
1.查找出现次数大于n/2的重复元素
《编程之美》中有同样的一道题《寻找发帖水王》,具体思路是每次删除两个不同的元素,最后剩下的就是要求的元素。这个结论的证明如下:
已知:n,m是正整数,n表示数组的长度,m是出现次数大于n/2的元素的个数,即m>n/2。
需要求证的结论包括两个:
(1)我们用v表示出现次数大于n/2的元素。当删除两个不同元素,且其中有一个元素是v时,则m减小1,同时n要减小2。
求证:m-1>(n-2)/2
证明:m-1>n/2-1=(n-2)/2
(2)当删除两个不同元素,且其中有一个元素不是v时,则只需要n减小2。
求证:m>(n-2)/2 。这个结论是显然的。
代码如下:
int find(int array[], int n) { int candidate; int count=0; for(int i=0;i<n;++i) { if(count==0) { candidate=array[i];count=1; } else { if(candidate==array[i]) ++count; else --count; } } return candidate; }
上述代码是错误的,最后还要验证一下candiate是不是的出现次数是大于n/2的。反例,1,2,3,最后剩下的是3,但是他不是我们要的结果。
《编程之美》的后面习题是“查找出现次数大于n/4的元素”,思路是每次删除不同的4个元素,最后剩下的3个就是候选元素,但是还要验证这3个元素是否满足条件。不再详细解释。其实《编程之美》里讲的方法就是本文后提到的“多重集”算法。
对于大于n/4的元素,最多有3个候选人,我们就设置3个candidate,每次同时删掉4个元素,其实是3个candidate同时减1。对剩下的3个元素检验是否是我们想要的结果即可。
推广到找到大于n/k的情况,设置(k-1)个候选。