Boyer–Moore选举算法是一个经典的用于找一组元素中出现次数超过元素个数一半的算法,该算法有线性时间复杂度和常数空间复杂度,是从现实生活中的选举问题抽象出来的,且看代码:
public class Solution {
public int majorityElement(int[] nums) {
int major = nums[0], count = 1;
for(int i = 1; i < nums.length; i++) {
if(major == nums[i])
count++;
else if(count == 0) {
count = 1;
major = nums[i];
}
else
count--;
}
return major;
}
}
对这个算法的疑惑集中于变量count的意义。我们可以想象以下的场景:在一个广场上有n个选民,他们心里都有一个已经选定的,非常坚定支持的候选人,还有一个主持这次选举的人。这n个选民中有这样一个庞大的隐藏的群体,他们都十分认可A这个人的工作能力,并且他们的总数已经超过了n/2。是的,这些选民选择的A正是我们要找的人。
接下来看看选举的过程吧,我们先构造一个对A最不利的场景,就是这些选民中除了A的支持者外其他的都是B的支持者,所以大多数情况下当主持人选一个选民念出他的A的支持者的时候,不出意外会有B的支持者迅速表示支持B,从而抵消A的这一票,所以最后我们只看相互抵消后剩余的票数就行了,由于A的支持者更多,所以A最后肯定胜出。可以看出来这正是Boyer–Moore选举算法的工作过程。
刚才的情况是对A最不利的一种,还有可能A的竞争者不止B一个人,有可能会有C,D等等,那么有时候还不等A的支持者出手,支持B的票就可能会被C或者D的支持者抵消,那最后统计剩余票数的时候不是A的更有优势嘛。
传统的Boyer–Moore选举算法是这样工作的,如果我们修改这个选举过程,选出得票数超过n/3的候选人,那应该怎么做呢?
首先应该想到,这样的候选人不能超过两个,如果有多于两个的候选人存在,那么一定是人群中有人偷偷的投了不止一票了,且看代码:
public class Solution {
public List<Integer> majorityElement(int[] nums) {
if(nums.length == 0) {
return new ArrayList<Integer>();
}
int k1 = nums[0], k2 = nums[0], c1 = 1, c2 = 0;
for(int v : nums) {
if(v == k1) {
c1++;
}
else if(v ==k2) {
c2++;
}
else if(c1 == 0) {
k1 = v;
c1 = 1;
}
else if(c2 == 0) {
k2 = v;
c2 = 1;
}
else {
c1--;
c2--;
}
}
c1 = 0; c2 = 0;
for(int v : nums) {
if(v == k1) c1++;
if(v == k2) c2++;
}
List<Integer> arr = new ArrayList<Integer>();
if(c1 > nums.length/3) arr.add(k1);
if(c2 > nums.length/3) arr.add(k2);
return arr;
}
}
思路与之前如出一辙,需要注意c2的初始化值是0,而不是1,这样做是为了避免当输入是[1],或者是[1,2],的时候出现意外的结果。