现在的位置: 首页 > 综合 > 正文

【编程之美】读书笔记:寻找发帖水王

2014年01月01日 ⁄ 综合 ⁄ 共 1956字 ⁄ 字号 评论关闭

       问题:传说,Tango有一大“水王”,他不但喜欢发帖还会回复其他ID发的每个帖子。该“水王”的帖子数目超过了帖子总数的一半,如何用快速的方法找出这个“水王”的ID?

         分析:首先想到的是对所有的ID进行排序,然后再扫描一遍排好序的ID列表,统计各个ID出现的次数。如果某个ID的次数超过总数的一半,那么就输出这个ID。这个算法的时间复杂度为O(N*logN+N).

               如果ID列表是已经排好序,则不需要重复扫描整个列表了。如果一个ID出现的次数超过总数N的一半。那么无论水王的ID是什么,这个有序的ID列表的第N/2 项(从0开始编号)一定会是这个水王的ID。出去排序的的时间复杂度,后处理需要的时间为O(1)。

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

             如果每次删除两个不同的ID(不管是谁的ID),那么,在剩下的ID列表中,“水王”的ID出现的次数仍然超过总数的一半。按照这个原理,就可以不断重复这个过程,把ID列表中的ID总数降低,从而得到问题的答案。这个算法的时间复杂度只需要O(N),并且只需要常数的额外内存。

          算法实现: 数组中有个数字出现的次数超过了数组长度的一半。也就是说,有个数字出现的次数比其他所有数字出现次数的和还要多。因此我们可以考虑在遍历数组的时候保存两个值:一个是数组中的一个数字,一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1。 如果下一个数字和我们之前保存的数字不同,则次数减1。如果次数为零,我们需要保存下一个数字,并把次数重新设为1。  
        下面,举二个例子:

  • 第一个例子,5,5,5,5,1 :

    不同的相消,相同的累积。遍历到第四个数字时,candidate 是5, nTimes 是4;    遍历到第五个数字时,candidate 是5, nTimes 是3;  nTimes不为0,那么candidate就是超过半数的。 

  • 第二个例子,0,1,2,1,1:

    开始时,保存candidate是数字0,ntimes为1,遍历到数字1后,与数字0不同,则ntime减1变为零,;接下来,遍历到数字2,2与1不同,candidate保存数字2,且ntimes重新设为1;继续遍历到第4个数字1时,与2不同,ntimes减一为零,同时candidate保存为1;最终遍历到最后一个数字还是1,与我们之前candidate保存的数字1相同,ntime加一为1。最后返回的是之前保存的candidate为1

 

代码如下:

 int Find(int * ID, int N)
{
int  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;
}

例如:a[]={0,4,4,1,4,4,5,4},输出为4。

 

扩展问题:若三个发帖很多的ID,他们的发帖数目都超过了帖子总数目N的1/4,怎么从发贴的ID中快速找出他们的ID?

typedef int Type;
Type candidate1 ;
Type candidate2;
Type candidate3;

void Find(Type* ID, int N)
{
    int nTimes1 = 0 ;
    int nTimes2 = 0 ;
    int nTimes3 = 0 ;
    int i;

    for( i = 0; i < N; i++)
    {
        if (nTimes1 == 0)
        {
            candidate1 = ID[i],nTimes1 = 1;
        }
        else
        {
            if (candidate1 == ID[i])
            {
                nTimes1++;
            }
            else
                if (nTimes2 == 0)
                {
                    candidate2 = ID[i],nTimes2 = 1;
                }
                else
                {
                    if (candidate2 == ID[i])
                    {
                        nTimes2++;
                    }
                    else
                    {
                        if (nTimes3 == 0)
                        {
                            candidate3 = ID[i], nTimes3 = 1;
                        }
                        else
                            if (candidate3 == ID[i])
                            {
                                nTimes3++;
                            }
                            else
                            {
                                nTimes1--;
                                nTimes2--;
                                nTimes3--;
                            }
                    }
                }
        }
    }
}

int main()
{
    int a[] = {0,4,1,4,0,4,1,4,1,0,3,3,0,3,3,3};
    Find(a,16);
    cout << candidate1 <<" " << candidate2 << " " << candidate3 << endl;
    return 0;
}

 

抱歉!评论已关闭.