题目:
三人行设计了一个灌水论坛。信息学院的学生都喜欢在上面交流灌水,传说在论坛上有一个“水王”,他不但喜欢发帖,还会回复其他ID发的每个帖子。坊间风闻该“水王”发帖数目超过了帖子数目的一半。如果你有一张当前论坛的帖子(包括回帖)列表,其中帖子的作者的ID也在其中,你能快速的找到这个传说中的水王吗?
设计思想:
通过对帖子列表进行遍历,统计水王帖子的数目是最简单的一种查找水王的方式,但是这种算法时间复杂度比较高。
采用优化的算法来解决这道问题,可以通过两两比较的方式来查找水王。如果当下正在比较的两个ID不同,则将其删除,(不论水王的ID是否在这两个之中),剩下的列表中,“水王”ID出现的次数仍然超过剩余总数的一半。通过不断的重复这个过程,列表中的记录数目就可以逐渐减少,从而在整体上降低算法的时间复杂度。
程序概要设计:
我的程序中首先假设列表中有十个用户ID,用户的ID号分别用0-9十个数字字符来代替,利用随机函数,生成N条记录,即将用户的ID号保存在一个长度为N的一维数组中。程序的起始同样用随机数生成了一个自定义的“水王”,在后续的程序中通过不重复随机替换的方式,确保了一维数组中的自定义“水王”ID号在一半以上。查找水王的核心程序利用上述思想完成,程序的最后加了一个判断,通过这个判断可以清楚地知道自己的找水王程序是否得到了正确的结果。
程序详细设计见源代码。
源代码如下:
//现有一张灌水论坛的帖子列表,“水王”发帖的数目超过了帖子数目的一半,找出水王ID
//05-19,2016,Hailin Song
#include<iostream>
#include<stdlib.h>
#include<time.h>
using namespace std;
#define N 100
int main()
{
srand((int)time(NULL)); //定义时间种子
int InitShuiWang = 0 + rand() % 10; //假设帖子列表中有10个ID号,分别用0-9这十个数字字符代替,自定义“水王”的ID
cout << "自定义的“水王”ID为:";
cout << InitShuiWang << endl; //输出自定义的“水王”ID,同时测试“找水王”程序的结果是否正确
cout << endl;
int Tiezi[N]; //假设帖子列表中有N条记录
int location[N / 2 + 1];
for (int i = 0; i < N; i++)
{
Tiezi[i] = 0 + rand() % 10;
}
for (int i = 0; i < (N/2+1); i++) //循环执行(N/2+1)次,确保帖子列表中有一半以上为“水王”的ID
{
location[i] = 0 + rand() % N; //确定帖子列表中随机替换的位置
for (int j = 0; j < i; j++)
{
if (location[i] == location[j]) //确保随机替换的位置不会发生重复
{
location[i] = 0 + rand() % N;
j = -1;
}
}
Tiezi[location[i]] = InitShuiWang; //用“水王”的ID替换随机产生的用户的ID
}
cout << "帖子列表的ID如下所示:" << endl;
for (int i = 0; i < N; i++)
{
cout << Tiezi[i]<<" ";
if (i % 10 == 9)
{
cout << endl;
}
}
cout << endl;
//找水王程序的核心代码,基本思想为:通过比较,每次删除两个不同的ID(不论是否包括水王),在剩下的列表中水王的ID出现次数仍在一半以上
int SelectShuiWang; //通过程序找出的“水王”的ID
int count=0; //用count记录“水王”在帖子列表中出现的次数
for (int i = 0; i < N; i++)
{
if (0 == count) //如果count为零,则表示当下还未找到真正的“水王”,或“水王”目前出现的次数小于等于其他ID用户出现的次数
{
SelectShuiWang = Tiezi[i];
count = 1;
}
else
{
if (SelectShuiWang == Tiezi[i])
{
count++;
}
else
{
count--;
}
}
}
cout << "找水王程序找出的“水王”ID为:" << SelectShuiWang << endl;
if (InitShuiWang == SelectShuiWang) //判断找水王程序是否运行正确,是否能够找出正确的“水王”ID
{
cout << "程序运行结果正确!" << endl;
}
else
{
cout << "程序运行结果错误!" << endl;
}
cout << endl;
return 0;
}
程序运行结果截图如下所示:

个人总结:
找水王算法和前段时间写的动态规划算法一样,都是非常经典,非常优化的算法。其实这两个算法之间有一些想通之处,都采用了一种转化的方式来把问题简化。当我们采用传统的思想思考问题时,虽然实现起来非常简单,但是那样的算法使得计算机需要做很多重复的工作。以本道题为例,优化的算法抓住了简化问题的一个最基本的点,那就是当删除两个不同的ID号时,无论水王的ID号在不在这两个之间,剩下的ID号列表中,水王的ID号仍然占据着一半以上。因此,这样就可以把列表中的ID号数量逐渐减少,从而提升程序的执行效率。程序就是这样,采用不同的思想以及不同的算法,有时甚至是几条不同的语句就能大大改善程序的性能,要想做到这一点,还需要从日后的学习中不断地积累。