题目:有一个人在论坛上发的帖子数超过了帖子总数的一半,每个帖子作者都有ID,怎么样迅速找出这个水王?
解法一:先将所有ID排序,再扫描一遍求得每个ID出现的次数。
解法二:先将所有ID排序,在N / 2处的ID一定是水王的。
解法三:
Type Find(Type* ID, int N) { Type candidate; int nTimes, i; for(i=nTimes=0;i<N;i++) { if(nTimes==0) { candidate = ID[i]; nTimes = 1; } else { if(candidate == ID[i]) nTimes++; else nTimes--; } } return candidate; }
总体思想是:遍历一次整个列表,利用一个计数器,每遇到两个不同的ID,就将它们排除在考虑之外。不管排除的这两个ID是否包含水王的ID,在每次排除之后,水王的ID次数还是超过总数的一半。
在代码里面,candidate变量最后返回的是水王的ID。nTimes是指当前candidate对应的ID在与不同ID抵消后的重数。比如有XXXYZ序列。那么当i=2时,nTimes=3,说明candidate(X)当前抵消后有3个重数;当i=3时,那么nTimes=2,说明遇到了一个不同的ID,导致candidate和这个不同ID被排除在考虑之外了。当i=4时,nTimes=1,这说明X这个ID的数量是超过一半的,因为它与所有不同的ID抵消之后还有重数。candidate最后返回的就是与所有ID抵消后重数还大于0的那个ID。
再考虑YXZXX序列。这次candidate换了几次,分别是Y,Z和最后一个X。
Y | X | Z | X | X | |
candidate | Y | Y | Z | Z | X |
nTimes | 1 | 0 | 1 | 0 | 1 |
最后考虑YZXXX序列。
Y | Z | X | X | X | |
candidate | Y | Y | X | X | X |
nTimes | 1 | 0 | 1 | 2 | 3 |
题目的关键是“超过一半”,所以可以通俗地认为水王的ID经得起抵消。