【编程之美学习笔记】2.3寻找发帖水王

003E5CD0坊间风闻“水王”发帖数目超过帖子总数的一半,如果你有一个当前论坛上所有帖子的列表,其中帖子作者的ID也在表中,你能快速找出这个传说中的水王吗?

题意即找出数组中超过一半的数

最直接的方法是对所有ID排序,然后再扫描一遍排好的ID列表,统计各个ID出现的次数,若某个ID次数超过总数的一半则输出这个ID,这个算法的时间复杂度为O(N * log2N + N)。

但事实上,若ID列表有序,不必再次扫描列表。若一个ID出现的次数超过总数N的一半,那么这个有序的ID列表中的第N/2项(从0开始编号)一定会是这个ID。这样能迅速定位到列表的某一项的话(比如用数组),该时间复杂度为O(N * log2N )。

但上面两种方法都要先对ID列表进行排序,时间复杂度方面没有本质的改进,能否避免排序呢?

如果每次删除 2 个不同的ID(不管是否包含“水王”的ID),那么在剩下的ID列表中,“水王”的ID出现的次数仍然超过总数的一半。可以通过不断重复这个过程把ID列表中的ID总数降低(转化为更小的问题),从而得到答案。

这个思路,避免了排序这个耗时的步骤,总的时间复杂度只有O(N),且只需要常数的额外内存。

 1 Type Find(Type *ID, int N){
 2     Type candidate;
 3     int nTimes, i;
 4     for(i = nTimes = 0; i < N; i++){
 5         if(nTimes == 0){
 6             candidate = ID[i], nTimes = 1;
 7         }
 8         else{
 9             if(candidate == ID[i])
10                 nTimes++;
11             else
12                 nTimes--;
13         }
14     }
15     return candidate;
16 }
View Code

这个题目体现了计算机科学中很普通的思想,就是如何把一个问题转化为规模较小的若干个问题。分治、递推和贪心等都是基于这样的思路。在转化的过程中,小的问题跟原问题本质上一致。同样我们可以将小问题转化为更小的问题,因此转化过程是很重要的。像上面这题,我们保证了问题的解在小问题中仍然具有与原问题相同的性质:水王的ID在ID列表中的数量超过一半。转化的效率越高,转化之后问题规模缩小得越快,则整体的时间复杂度越低。

扩展问题

现在有3个发帖很多的ID,他们的发帖数目都超过了帖子总数目N的1/4,你能从发帖ID列表中快速找出他们的ID吗?

思路与本题一样用抵消法,每次删除 4 个不同的ID即可。

 1 Type candidate[3];
 2 void Find1(Type *ID, int N) {
 3     int nTimes[3] = {0};
 4     for(int i = 0; i < N; ++i) {
 5         if(nTimes[0] == 0) {
 6             candidate[0] = ID[i];
 7             nTimes[0] = 1;
 8         }else if(nTimes[1] == 0 && ID[i] != candidate[0]) {
 9             candidate[1] = ID[i];
10             nTimes[1] = 1;
11         }else if(nTimes[2] == 0 && ID[i] != candidate[0] && ID[i] != candidate[1]) {
12             candidate[2] = ID[i];
13             nTimes[2] = 1;
14         }else{
15             if(candidate[0] == ID[i])
16                 nTimes[0]++;
17             else if(candidate[1] == ID[i])
18                 nTimes[1]++;
19             else if(candidate[2] == ID[i])
20                 nTimes[2]++;
21             else{
22                 nTimes[0]--;
23                 nTimes[1]--;
24                 nTimes[2]--;
25             }
26         }
27     }
28 }
View Code
原文地址:https://www.cnblogs.com/GraceSkyer/p/5843555.html