n问题描述:
给定一个论坛所有帖子的列表,其中帖子作者的id也在列表中,水王的发帖数目超过了所有帖子的半,请设计算法,找出水王!
问题分析:
该问题很直接,简化一下,就是找一个数组中,出现次数最多的数,其中,这个出现次数最多的数的出现次数占整个数组大小>50%。
最直观的方法是对数组进行排序,然后遍历,即可找出出现次数最多的数。总的时间复杂度为NlogN+N。
那么能不能不进行排序而用一个比较的方法找到这个出现次数最多的数?
注意一个性质——出现的次数大于整个数大小的组一半。仔细想想这个性质,如果每次删除两个不同的id,一样可以保证这个原来出现次数最多的数在删除两个不同的数后,依然满足关系,依次类推,不断缩小数组的大小,最后就可以找到这个出现次数最多的数。
这种思想很巧妙,但是不难想象。更精髓的地方在于如何将这个思想用代码实现。
设想一下,从数组的第一个数开始,如果第二个数是不同的,那么我们之前把指针移到第三个数,表示前面两个数已经被删除了;但是如果第二个数和第一个数相同,那么该怎么办?书中的伪码是这样做的,找第三个和第四个数,如果第三和第四都不同于第一个(第二个数,第一个数等于第二个数),则都删除,相当于删除了两队。
程序的巧妙之处在于用一个变量记录一个数的出现次数,第一次出现num=1,如果下一个数是一样的,则num++,否则则num--,当num = 0,说明了如果这个数出现了n,那么当n=0时,一定有n个数与之相同,这全部删除,相当于删除了n对!
伪代码:
Type Find(Type *ID,int N)
{
Typecandidate;
Intnum,I;
For(I = num = 0;I < N; i++)
{
If(num == 0)
{
Candidate = ID[i];
Num++;
}
Else
{
If(candidate == ID[i])
Num++;
Else
Num--;
}
}
Returncandidate;
}
书中的代码
If(num == 0)
{
Candidate = ID[i];
}
我改为:
If(num == 0)
{
Candidate = ID[i];
Num++;
}
不然永远不能进入1级else
我用自己的程序验证了这一点,结果如下: