摩尔投票法(多数投票法)

摩尔投票:主要解决的问题,如何在任意多的候选人中(选票无序),选出获得票数最多的那个。

时间复杂度:O(N)
空间复杂度:O(1)

形象化描述:
在这里插入图片描述

想象着这样一个画面:会议大厅站满了投票代表,每个都有一个牌子上面写着自己所选的候选人的名字。然后选举意见不合的(所选的候选人不同)两个人,会打一架,并且会同时击倒对方。显而易见,如果一个人拥有的选票比其它所有人加起来的选票还要多的话,这个候选人将会赢得这场“战争”,当混乱结束,最后剩下的那个代表(可能会有多个)将会来自多数人所站的阵营。但是如果所有参加候选人的选票都不是大多数(选票都未超过一半),那么最后站在那的代表(一个人)并不能代表所有的选票的大多数。因此,当某人站到最后时,需要统计他所选的候选人的选票是否超过一半(包括倒下的),来判断选票结果是否有效。

算法步骤:

分为两个阶段:对抗(pairing)阶段和计数(counting)阶段
对抗阶段: 两个不同选票的人进行对抗,并会同时击倒对方,当剩下的人都是同一阵营,即相同选票时,结束。
计数阶段: 对最后剩下的人进行选票统计,判断选票是否超过总票数的一半,选票是否有效。

对抗阶段的简化:
在这里插入图片描述
我们不需要选票不同就大干一架,我们可以采取一个更加文明的方式。在场有个很聪明的人,他目光扫了一遍所有的选票,在脑子里记住了两件事,当前的候选人的名字cand和他对应的计数器k(k初始化为0,并不是cand的选票数)。看每个人的选票时,先看下k是否为0,如果为0就将cand更新为他看到的马上看到的候选人的姓名,并将k的值更新为1,此时的k代表的是cand更新后人的阵营人数。观察每个人的选票的过程,如果这个人的选票和cand相同,则将k的值+1,否则,将k的值-1,最后的cand可能胜选,还需要统计他的总票数是否超过一半。

算法证明:

在这里插入图片描述
假设共有n个代表(一人一票,选票总数为n)。当聪明人看到第i个代表的选票时( 1 ≤ i ≤ n ) ,前面他已经看到的所有选票可以分为两组,第一组是k个代表赞同cand;另一组是选票可以全部成对(选票不同)抵销。当处理完所有的选票时,如果存在大多数,则cand当选。
假设存在一个x其不同于cand,但拥有的选票超过n / 2。但因为第二组的选票可以全部成对抵销,所以x最多的选票数为( n − k ) / 2 ,因此x必须要收到第一组的选票才能超过一半,但是第一组的选票都是cand的,出现矛盾,假设不成立。
所以,如果存在大多数,cand就是那个。

算法演示:
网页链接

代码框架:

    public int majorityElement(int[] nums) {
    
    
        int cand = 0, k = 0;
        //对抗阶段,最后的cand有可能就是选票最多的候选人
        for (int i = 0; i < nums.length; i++){
    
    
            if(k == 0){
    
    
                cand = nums[i];
                k = 1;
            }
            else {
    
    
                if(nums[i] == cand){
    
    
                    k += 1;
                }
                else {
    
    
                     k--;
                }
            }
        }
        //计数阶段,看是否是大多数
        int cnt = 0;
        for (int num:nums) {
    
    
            if(num == cand)
                cnt++;
        }
        //表示未超过一半
        if (cnt <= nums.length/2){
    
    
            cand = -1;
        }
        return cand;
    }

猜你喜欢

转载自blog.csdn.net/Puppet__/article/details/115296697