用“打架”来理解摩尔投票法,生动形象一看就懂
1.问题提出
摩尔投票法,最简单形象的理解,就是一群人去抢座位。
在某个战乱年代,有m个来自不同阵营的人,要去争抢n个王座,大家都想抢,怎么办呢,那肯定是谁的人多,谁就可以坐上王座呗。比如只有1个王座的话,那人数必须要大于总人数一半(1 / 2)的阵营才可以坐,否则其他阵营的人联合起来就可以把你搞死;有2个王座的话,那人数必须要大于总人数的 (1/3)的两个阵营可以坐,有n个王座,那阵营人数必须大于m * (1 / n)。
那么这个问题抽象化就是:如何从一个长度为m的数组中,找到出现次数大于(1 / n) * m次的数?
2.一个王座(n = 2)
A A B C B A A
假设现在有如上7个人来自A B C三个阵营,要抢一个王座,那么按照顺序:
-
首先是A,一来发现王座是空的,那他就坐上去,此时王座属于A阵营
-
下一个又是A,发现自家已经占了王座了,针不戳,于是王座的A阵营人员+1
-
然后是B,发现座位上有人了,那不行,得打一架,于是跟一个A打了一架,两个人同归于尽了,不过王座上还有一个A
-
接下来是C,发现座位上有人了,那不行,得打一架,于是跟一个A打了一架,两个人同归于尽了,这个时候王座就空了
-
然后又是B,发现王座是空的,就坐上去
-
然后又依次来了两个A,过程略。
所以最后占有王座的,就是A阵营了。伪代码如下:
int num = 0;//王座是谁家的
int count = 0;//王座上的人数
for(int i : nums){
if(count == 0) num = i;//王座是空的 i坐上去
if(num == i) count++;//i阵营人数+1
else count--;//打一架
}
//计数阶段略
return num;
3.两个及以上王座(n > 2)
A B B C B C A A
对于这种情况,以两个王座为例,两个以上的同理:
-
首先A来了,两个王座都是空的,就坐到其中一个上
-
然后来了个B,发现两个王座有一个被A占了,但是还有一个是空的,那算了,多一事不如少一事,我坐这个是一样的,于是B坐在另一个王座上
-
然后又来了个B,看到B阵营已经占据了一个王座,赶紧加入了进去
-
这个时候来了个C,看到两个王座都有人了,很生气,要跟A和B打一架,于是A派一个人,B派一个人,这三个人同归于尽了。那么这个时候原本A坐的王座就空出来了
哎,有同学可能会问了,为啥A和B都要派一个人跟C打?C去抢A的,B在旁边看戏不行么?其实不是的。考虑一下如果只有A B C三个人,两个位置,A和B先坐了,这个时候A和B是处于一种投票的平衡状态,你一半我一半嘛。但是如果C来了,这种平衡就被打破了,变成了每个人占1/3,那两个位置谁能坐呢?谁都不能坐,所以这时候这三个人就只能同归于尽。也就是说,C的加入对A和B的影响是同时产生的,会打破A和B原本的投票平衡状态,A阵营和B阵营为了维持自己的状态,都得派一个人出来,然后和新来的C中和掉。
- 接着来了B,于是加入到B的王座
- 然后又来了个C,看到有个王座是空的,好,然后他就坐了上去,这个时候两个王座分别由B阵营(2人)和C阵营(1人)占有
- 然后来了个A,A B C三家又打了一架,C的王座空了
- 最后来了个A,坐到了空的王座上。
因此,最后由A阵营和B阵营拿下这两个王座。伪代码如下:
int num1 = 0, num2 = 0;//两个王座
int count1 = 0, count2 = 0;//都是空的
for(int i : nums){
if(i == num1)//哟 我家已经占了第一个王座了
count1++;
else if(i == num2)//我家虽然没占到第一个,但是占到了第二个
count2++;
else if (count1 == 0){//有一个王座是空的
num1 = i;
count1 = 1;
}else if(count2 == 0){//另一个王座是空的
num2 = i;
count2 = 1;
}else{//打一架
count1--;
count2--;
}
}
//计数阶段略
其他
摩尔投票还有一个计数阶段,不过这个比较简单,我这里就不讲了。n = 2
时的摩尔投票看代码还算好理解,但是当n > 2
时,很多同学就看不太懂了(包括我一开始也没看懂)。不过后来我把抽象问题具体化,一下就想通了,因此写了这篇博文记录一下。最后希望大家在生活中和平相处,不要打架。