[笔记]《编程之美》2.3 寻找发帖“水王”

题目

Tango是微软亚洲研究院的一个试验项目. 研究院的员工和实习生们都很喜欢在Tango上面交流灌水. 传说, Tango有一大"水王", 他不但喜欢发贴, 还会回复其他ID发的每个帖子. 坊间风闻该"水王"发帖数目超过了帖子总数的一半. 如果你有一个当前论坛上所有帖子(包括回帖)的列表, 其中帖子作者的ID也在表中, 你能快速找出这个传说中的Tango水王吗?

思路

3, 1, 3, 1, 5, 1, 6, 1, 2, 1, 1, 1, 2

input | candidate | nTimes
      |     -1    |   0
   3  |      3    |   1
   1  |      3    |   0
   3  |      3    |   1
   1  |      3    |   0
   5  |      5    |   1
   1  |      5    |   0
   6  |      6    |   1
   1  |      6    |   0
   2  |      2    |   1
   1  |      2    |   0
   1  |      1    |   1
   1  |      1    |   2
   2  |      1    |   1

前提是“水王”的ID在ID列表中的次数超过一半,刚好等于一半是不行的

def find(A):
    candidate = -1
    nTimes = 0
    for i in range(0, len(A)):
        if nTimes == 0:
            candidate = A[i]
            nTimes += 1
        elif candidate == A[i]:
            nTimes += 1
        else:
            nTimes -=1
    return candidate

A = [3, 1, 3, 1, 5, 1, 6, 1, 2, 1, 1, 1, 2]
print(find(A))

扩展问题

随着Tango的发展, 管理员发现, "超级水王"没有了. 统计结果表明, 有3个发帖很多的ID, 他们的发帖数目都超过了帖子总数目N的1/4. 你能从发帖ID列表中快速找出他们的ID吗?

#-*-coding:utf-8-*-
def find(A):
    candidate = [-1] * 3
    nTimes = [0] * 3
    for i in range(0, len(A)):
        is_candidate = False
        for j in range(0, 3):
            # 第一种情况:A[i]不在候选项中,且有计数器为0的项,则设置A[i]为新的候选项
            if nTimes[j] == 0 and not A[i] in candidate:
                is_candidate = True
                candidate[j] = A[i]
                nTimes[j] = 1
                break
            # 第二种情况:A[i]在候选项中,则把对应计数器加1
            elif candidate[j] == A[i]:
                is_candidate = True
                nTimes[j] += 1
                break
        # 第三种情况: A[i]不在候选项中, 且没有计数器为0的项, 则所有计数器减1
        if not is_candidate:
            for j in range(0, 3):
                nTimes[j] -= 1
        # print(A[i], candidate, nTimes)
    return candidate

# A = [1, 2, 3, 2, 1, 2, 3, 3, 1, 2, 3, 6, 1, 1, 2, 3, 1, 2, 3, 8]
A = [2, 2, 3, 2, 3, 3, 1, 1, 7, 1, 1, 3, 3, 10, 6, 1, 2, 2, 2, 8, 1, 3, 10, 1, 3, 2, 8, 2, 1, 3, 9, 1, 3, 1, 3, 2, 10, 1, 2, 3, 2, 1, 4, 13, 3, 9, 2, 3, 2, 1]
# 验证用
# print(set([x for x in A if A.count(x) > len(A) / 4]))
print(find(A))

另外一种Python的简洁写法:

>>> a = [3, 1, 3, 1, 5, 1, 6, 1, 2, 1, 1, 1, 2]
>>> print set([x for x in a if a.count(x) > len(a)/2])
set([1])
>>> b = [2, 2, 3, 2, 3, 3, 1, 1, 7, 1, 1, 3, 3, 10, 6, 1, 2, 2, 2, 8, 1, 3, 10, 1, 3, 2, 8, 2, 1, 3, 9, 1, 3, 1, 3, 2, 10, 1, 2, 3, 2, 1, 4, 13, 3, 9, 2, 3, 2, 1]
>>> print set([x for x in b if b.count(x) > len(b)/4])
set([1, 2, 3])

【练习Java, 方法同上面】

【笔试题】数组中有一个数字出现的次数超过了数组长度的一半, 找出这个数字.

public class Hello {
    public static int find(int[] arr) {
        int candidate = -1;
        int ntimes = 0;
        int arr_len = arr.length;
        for (int i = 0; i < arr_len; i++) {
            if (ntimes == 0) {
                candidate = arr[i];
                ntimes++;
            } else if (arr[i] == candidate) {
                ntimes++;
            } else {
                ntimes--;
            }
        }
        return candidate;
    }

    public static void main(String[] args) {
        int[] arr = { 3, 1, 3, 1, 5, 1, 6, 1, 2, 1, 1, 1, 2 };
        System.out.println(find(arr)); // 1
    }
}