编程之美---发帖“水王”扩展问题

扩展问题一:

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

问题求解:

上题只需要一个结果,而现在需要3个结果,所以我们考虑数组作为返回值,同时,上题用到的nTimes,也应改为一个大小为3的数组。我们要如何保证最终返回的数组的3个元素就是3个灌水最多的用户呢?首先分析其所占比例,各超过1/4,也就是说剩下的其它ID所占帖数就不足总帖数1/4了。现在我们需要3个变量来记录当前遍历过的3个不同的ID,而nTimes的3个元素分别对应当前遍历过的3个ID出现的个数。如果遍历中有某个ID不同于这3个当前ID,我们就判断当前3个ID是否有某个的nTimes为0,如果有,那这个新遍历的ID就取而代之,并赋1为它的遍历数(即nTimes减1),如果当前3个ID的nTimes皆不为0,则3个ID的nTimes皆减去1,这也就是解决本文题的关键了。由于非水王ID不满总帖数的1/4,与上题思路相同,所遍历ID与当前3个ID不同时,就一同抵消(即3个当前ID的nTimes值减1),最终留下来的3个当前ID总会是3个超过1/4的水王ID。

#include <iostream>
using namespace std;
typedef int Type;

void Find(Type* ID, int N)
{//ID代表数组,N代表数组长度
    Type candidate[3] = {NULL};
    int nTimes[3]={0};
    for(int i=0;i<N;i++)
    {
        if(candidate[0] == ID[i])
        {
            nTimes[0]++;
        }
        else if(candidate[1] == ID[i])
        {
            nTimes[1]++;
        }
        else if(candidate[2] == ID[i])
        {
            nTimes[2]++;
        }
        else if(nTimes[0] ==0 && candidate[1] != ID[i] && candidate[2] != ID[i])
        {
            candidate[0]=ID[i], nTimes[0]=1;
        }
        else if(nTimes[1] ==0 && candidate[0] != ID[i] && candidate[2] != ID[i])
        {
            candidate[1]=ID[i], nTimes[1]=1;
        }
        else if(nTimes[2] ==0 && candidate[0] != ID[i] && candidate[1] != ID[i])
        {
            candidate[2]=ID[i], nTimes[2]=1;
        }
        else if(candidate[0] != ID[i] && candidate[1] != ID[i] && candidate[2] != ID[i])
        {
            nTimes[0]--;
            nTimes[1]--;
            nTimes[2]--;
        }
    }
    cout<<candidate[0]<<" "<<candidate[1]<<" "<<candidate[2]<<endl;
    return;
}
int main()
{
    Type a[] = {1,1,2,2,3,4,5,5};
    cout<<"The candidate is : "<<endl;
    Find(a,8);
    cout<<endl;
    return 0;
}

执行结果:

The candidate is :
1 2 5

扩展问题二:

我们知道,水王问题:有N个数,其中有一个数出现超过一半,要求在线性时间求出这个数。那么,我的问题是,加强版水王:有N个数,其中有一个数刚好出现一半次数,要求在线性时间内求出这个数。
转自http://blog.csdn.net/v_july_v/article/details/6890054

问题求解:

如果是刚好出现一半的话,如此例: 0,1,2,1:
遍历到0 时 candidate为0,times为1
遍历到1时 与candidate不同,times减为0
遍历到2时, times为0,则candidate更新为2,times加1
遍历到1时, 与candidate不同,则times减为0;我们需要返回所保存candidate(数字2)的下一个数字即数字1。
所以,如果还运用寻找普通“水王”的程序的话,那么只能返回我们要找的数字1的前一个数字,即遍历到1之前所保存的candidate2,试问如何让程序能返回我们需要的数字1呢?

用两个变量来记录水王,最终完整代码如下(测试正确):

#include <iostream>
using namespace std;
typedef int Type;

Type Find(Type* ID, int N)
{//ID代表数组,N代表数组长度
    Type candidate1, candidate2;
    int nTimes1, nTimes2;
    for(int i=nTimes1=nTimes2=0;i<N;i++)
    {
        if(nTimes1==0)
        {
            candidate1=ID[i],nTimes1=1;
        }
        else if(nTimes2==0 && candidate1 != ID[i])
        {//注意:这里的判断条件加上第二个变量是否等于第一个变量的判断
            candidate2=ID[i],nTimes2=1;
        }
        else
        {
            if(candidate1 == ID[i])
            {
                nTimes1++;
            }
            else if(candidate2 == ID[i])
            {
                nTimes2++;
            }
            else
            {
                nTimes1--;
                nTimes2--;
            }
        }
    }
    return nTimes1>nTimes2?candidate1:candidate2;
}
int main()
{
    int a[4]={0,1,2,1};
    cout<<"The candidate is : "<<endl;
    cout<<Find(a,4)<<endl;
    return 0;
}

你可能感兴趣的:(扩展,水王)