編程之美之尋找發帖「水王」 的算法問題

Tango是微軟亞洲研究 院的一個試驗項目。研究院的員工和實習生們都很喜歡在Tango上面交流灌水。傳說,Tango有一大「水王」,他不但喜歡發貼,還會回覆其餘ID發的每 個帖子。坊間風聞該「水王」發帖數目超過了帖子總數的一半。若是你有一個當前論壇上全部帖子(包括回帖)的列表,其中帖子做者的ID也在表中,你能快速找 出這個傳說中的Tango水王嗎?
分析與解法

首先想到的是一個最直接的方法,咱們能夠對全部ID進行排序。而後再掃描一遍排好序的ID列表,統計各個ID出現的次數。若是某個ID出現的次數超過總數的一半,那麼就輸出這個ID。這個算法的時間複雜度爲ON * log2N)。 算法

若是ID列表已是有序的,還須要掃描一遍整個列表來統計各個ID出現的次數嗎? 數組

若是一個ID出現的次數超過總數N的一半。那麼,不管水王的ID是什麼,這個有序的ID列表中的第N/2項(從0開始編號)必定會是這個ID(讀者能夠試着證實一下)。省去從新掃描一遍列表,能夠節省一點算法耗費的時間。若是可以迅速定位到列表的某一項(好比使用數組來存儲列表),除去排序的時間複雜度,後處理須要的時間爲O(1)。 spa

但上面兩種方法都須要先對ID列表進行排序,時間複雜度方面沒有本質的改進。可否避免排序呢? code

如 果每次刪除兩個不一樣的ID(無論是否包含「水王」的ID),那麼,在剩下的ID列表中,「水王」ID出現的次數仍然超過總數的一半。看到這一點以後,就可 以經過不斷重複這個過程,把ID列表中的ID總數下降(轉化爲更小的問題),從而獲得問題的答案。新的思路,避免了排序這個耗時的步驟,總的時間複雜度只 有ON),且只須要常數的額外內存。僞代碼以下: 排序

代碼清單: 內存

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

在 這個題目中,有一個計算機科學中很廣泛的思想,就是如何把一個問題轉化爲規模較小的若干個問題。分治、遞推和貪心等都是基於這樣的思路。在轉化過程當中,小 的問題跟原問題本質上一致。這樣,咱們能夠經過一樣的方式將小問題轉化爲更小的問題。所以,轉化過程是很重要的。像上面這個題目,咱們保證了問題的解在小 問題中仍然具備與原問題相同的性質:水王的ID在ID列表中的次數超過一半。轉化自己計算的效率越高,轉化以後問題規模縮小得越快,則總體算法的時間複雜 度越低。 class

擴展問題

隨着Tango的發展,管理員發現,「超級水王」沒有了。統計結果代表,有3個發帖不少的ID,他們的發帖數目都超過了帖子總數目N的1/4。你能從發帖ID列表中快速找出他們的ID嗎? 效率

參考上面的解法,思路以下:
若是每次刪除四個不一樣的ID(無論是否包含發帖數目超過總數1/4的ID),那麼,在剩下的ID列表中,原先發帖比例大於1/4的ID所佔比例仍然大於1/4。能夠經過不斷重複這個過程,把ID列表中的ID總數下降(轉化爲更小的問題),從而獲得問題的答案。
計算機科學

代碼以下: 擴展

void Find(Type* ID, int N,Type candidate[3])
{
    Type ID_NULL;//定義一個不存在的ID
    int nTimes[3], i;
    nTimes[0]=nTimes[1]=nTimes[2]=0;
    candidate[0]=candidate[1]=candidate[2]=ID_NULL;
    for(i = 0; i < N; i++)
    {
        if(ID[i]==candidate[0])
        {
             nTimes[0]++;
        }
        else if(ID[i]==candidate[1])
        {
             nTimes[1]++;
        }
        else if(ID[i]==candidate[2])
        {
             nTimes[2]++;
        }
        else if(nTimes[0]==0)
        {
             nTimes[0]=1;
             candidate[0]=ID[i];
        }
        else if(nTimes[1]==0)
        {
             nTimes[1]=1;
             candidate[1]=ID[i];
        }
        else if(nTimes[2]==0)
        {
             nTimes[2]=1;
             candidate[2]=ID[i];
        }
        else
        {
             nTimes[0]--;
             nTimes[1]--;
             nTimes[2]--;
         }
    }
    return;
}
相關文章
相關標籤/搜索