【鹅厂面试题】——-关于微信红包取中间值(笔试题)

春节期间小明使用微信收到很多个红包,非常开心。在查看领取红包记录时发现,某个红包金额出现的次数超过了红包总数的一半。请帮小明找到该红包金额。写出具体算法思路和代码实现,要求算法尽可能高效。 
给定一个红包的金额数组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;
    }
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

基于数组的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;
    }
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

该方法时间复杂度同样是O(N)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值