騰訊筆試題--微信紅包

FreeeLinux發表於2017-02-01

本來這題沒什麼難度,不值得我寫部落格的,誰知道一個失誤花了半小時才做出來。寫下來告誡自己不要再犯錯。

春節期間小明使用微信收到很多個紅包,非常開心。在檢視領取紅包記錄時發現,某個紅包金額出現的次數超過了紅包總數的一半。請幫小明找到該紅包金額。寫出具體演算法思路和程式碼實現,要求演算法儘可能高效。
給定一個紅包的金額陣列gifts及它的大小n,請返回所求紅包的金額。
若沒有金額超過總數的一半,返回0。

測試樣例:
[1,2,3,2,2],5
返回:2

其實就是求超過陣列一半的元素問題,劍指offer上有。

方法一:利用partition方法

class Gift {
public:
    int getValue(vector<int> gifts, int n) {
        if(n <= 0)
            return -1;
        int start = 0;
        int end = n - 1;
        int middle = (end - start) >> 1;
        int index = -1;
        index = partition(gifts, start, end);
        while(index < middle){
            if(index > middle){ //index>middle,說明中位數在左邊,我們需要左調整end
                end = index - 1;
                index = partition(gifts, start, end);
            }
            else{  //index <= middle,說明中位數在右邊,需要右調整start
                start = index + 1;
                index = partition(gifts, start, end);  

            }
        }
        return check_more_than_half(gifts, n, gifts[middle]);
    }
    int partition(vector<int> gifts, int start, int end){
        int small = start - 1; //在這行和下面這行,我想把我自己叫哥了,經常由於寫程式碼寫的快把start寫成0,因為這個耽擱時間我真是醉了...
        for(int index=start; index<end; ++index){
            if(gifts[index] < gifts[end]){
                ++small;
                if(index != small)
                    std::swap(gifts[index], gifts[small]);
            }
        }
        ++small;
        std::swap(gifts[small], gifts[end]);
        return small;
    }
    int check_more_than_half(vector<int>& gifts, const int n, int val){
        int cnt = 0;
        for(auto i : gifts){
            if(i == val)
                ++cnt;
        }
        return (cnt != 0 && cnt > (n >> 1)) ? val : 0;
    }
};

基於陣列的partition方法是成熟的用來求第k位數的方法,在此處我們用來求的是中位數。我們求得中位數,然後判斷中位數是否超過陣列的一半就得出答案了。

如果求第k個數,while迴圈條件改為index< k-1(或Index != k-1)即可。

對於每次partition得出的index,如果index>middle,說明中位數在index左邊(當前index所對的元素肯定大於中位數)。否則,中位數在index右邊。當index=middle時,成功求得中位數,會跳出迴圈。

這個方法的時間複雜度是O(N)。

另外,千萬注意partition中是start,不是0。

方法二:攻守陣地法

我們把第一個元素作為士兵,並使用一個計數變數times。遍歷陣列,如果出現相同元素,times++。如果出現不同元素,times–。當times減為0時,該士兵死亡。使用新的gifts[i]作為士兵,繼續攻守。最後留在戰場上的士兵有可能就是超過一半的數字。

最後利用check函式判斷即可。

程式碼如下:

class Gift {
public:
    int getValue(vector<int> gifts, int n) {
        if(n <= 0)
            return -1;
        int times = 1;
        int soldier = gifts[0];
        for(int i=1; i<n; ++i){
            if(soldier == gifts[i])
                ++times;
            else if(soldier != gifts[i]){
                if(--times == 0){
                    soldier = gifts[i];
                    times = 1;
                }
            }
        }
        return check_more_than_half(gifts, n, soldier);
    }
    int check_more_than_half(vector<int>& gifts, const int n, const int val){
        int cnt = 0;
        for(auto i : gifts){
            if(i == val)
                ++cnt;
        }
        return (cnt != 0 && cnt > (n >> 1)) ? val : 0;
    }
};

該方法時間複雜度同樣是O(N)。

相關文章