编程之美-寻找发帖水王

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

 

分析:如果一个ID出现的次数超过总数N的一半,那么先把这些ID排好序,那么这个有序ID列表的第N/2项肯定是水王ID,而且N/2的左边或者右边肯定全都是水王的ID

如果ID没排好序,那么每次删除两个不同的ID(不管是否包含水王的ID),那么两两对消之后,剩下的水王ID仍然是超过一半。这样子就可以通过重复这个过程把ID总数降低(转化为更小的问题)。到最后,剩下的就是水王的ID。这个方法避免了排序这个耗时的步骤,总的时间复杂度为O(N),而且只需要常数的额外内存。

 

在这个题目中,就是如何把一个问题转化为规模较小的若干个问题。分治、递推和贪心等都是基于这样的思路。在转化过程中,小的问题跟原本问题本质上是一致。这样,我们可以通过同样的方式将小问题转化为更小问题。因此,转化过程是很重要的。转化本身计算的效率越高,转化之后问题规模缩小得越快,则整体算法时间复杂度越低。

 

扩展问题:

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

 

数组:6,4,5,4,4,4

开始for数组:

初始化nTimescandidate0

如果nTimes0candidate=6nTimes=1

第二次由于nTimes不为0,而且candidate=1=4,那么nTimes-1=0

这样子就将第一个6和第二个4对消了。

 

下一轮由于nTimes已经为0,所以candidate=5,nTimes=1

到第四个数字4的时候,由于nTimes不为0,而且candidate=5=4,那么nTimes-1=0

此时第三个数字5和第四个数字4又对消了。

 

下一轮从从第五个数字4开始,

由于nTimes=0,所以candidate=5,nTimes=1

到最后一个数字4的时候,由于(candidate=4)==4,那么nTimes+1=2

 

代码:

View Code
  1 using System;
  2 
  3 namespace 寻找发帖水王
  4 {
  5     class Program
  6     {
  7         static void Main()
  8         {
  9             int[] ids = InitArray();
 10             int halfId = FindHalfId(ids, ids.Length);
 11             PrintIds(halfId);
 12 
 13             //int[] ids = { 4, 8, 4, 5, 6, 6, 5, 7, 6, 5, 4, 5, 4, 6, 9 };
 14             //int[] quarterIds = FindQuarterIds(ids, ids.Length);
 15             //PrintIds(quarterIds);
 16             Console.ReadLine();
 17         }
 18 
 19         static int[] InitArray()
 20         {
 21             int[] ids = new int[40];
 22             Random rd = new Random();
 23             int halfId = rd.Next(1111, 1141);
 24 
 25             //创建水王id出现次数
 26             for (int i = 0; i < 23; i++)
 27             {
 28                 ids[i] = halfId;
 29             }
 30             for (int i = 23; i < 40; i++)
 31             {
 32                 ids[i] = rd.Next(1111, 1141);
 33             }
 34             return ids;
 35         }
 36 
 37         static void PrintIds(dynamic t)
 38         {
 39             if (t is Array)
 40             {
 41                 foreach (var item in t)
 42                 {
 43                     Console.WriteLine("发帖超过1\\{0}的水王id:{1}:", t.Length + 1, item);
 44                 }
 45             }
 46             else
 47             {
 48                 Console.WriteLine("发帖超过一半的水王Id:{0}", t);
 49             }
 50         }
 51 
 52         static int FindHalfId(int[] ids, int len)
 53         {
 54             //创建临时id变量和计数器
 55             int candidate = 0, nTimes = 0;
 56 
 57             for (int i = 0; i < len; i++)
 58             {
 59                 //如果计数器为0,那么将id赋值给candidate,并设nTimes=1,即改id出现的次数
 60                 if (nTimes == 0)
 61                 {
 62                     candidate = ids[i];
 63                     nTimes++;
 64                 }
 65                 else
 66                 {
 67                     //如果本次出现的id跟candidate的相同,那么计数器加1,或者两两对消减1
 68                     nTimes = (candidate == ids[i]) ? nTimes + 1 : nTimes - 1;
 69                 }
 70             }
 71             return candidate;
 72         }
 73 
 74         //其实就是把这三个ID看成一个ID,然后跟另外一个ID对消(四个不同ID对消),对消之后他们的ID仍然会超过1/4
 75         static int[] FindQuarterIds(int[] ids, int len)
 76         {
 77             int[] candidate = new int[3];
 78             int[] nTimes = new int[3];
 79             for (int i = 0; i < len; i++)
 80             {
 81                 #region MyRegion
 82                 if (candidate[0] == ids[i])
 83                 {
 84                     nTimes[0]++;
 85                 }
 86                 else if (candidate[1] == ids[i])
 87                 {
 88                     nTimes[1]++;
 89                 }
 90                 else if (candidate[2] == ids[i])
 91                 {
 92                     nTimes[2]++;
 93                 }
 94                 else if (nTimes[0] == 0)
 95                 {
 96                     candidate[0] = ids[i];
 97                     nTimes[0] ++;
 98                 }
 99                 else if (nTimes[1] == 0)
100                 {
101                     candidate[1] = ids[i];
102                     nTimes[1]++;
103                 }
104                 else if (nTimes[2] == 0)
105                 {
106                     candidate[2] = ids[i];
107                     nTimes[2]++;
108                 }
109                 else
110                 {
111                     nTimes[0]--;
112                     nTimes[1]--;
113                     nTimes[2]--;
114                 }
115                 #endregion
116             }
117             return candidate;
118         }
119     }
120 }

 

 

 

转载于:https://www.cnblogs.com/silongxu/archive/2012/12/22/2829178.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值