问题:
有一个数组,里面是一些重复的数字。问是否存在一个数,这个数出现的次数超过了数组大小的一半?如果存在的话,这个数是多少?
例如对于数组[1,1,3,1,3,1,2],数字1的出现的次数超过了数组的一半;
而对于数组[6,6,6,7,7,7],没有数字超过数组的一半(数字6和7都出现了3次,出现的次数等于数组的一半,而没有超过数组的一半)
如果题目要求的是数字出现的次数大于等于数组的一半,那多数投票算法就失效了,只能用其他方法来实现。
解决方式:
对于这个问题,最佳的算法是多数投票算法,也叫作Boyer-Moore algorithm。
对这个问题,最大投票算法的时间复杂度是O(n)(总共遍历两次数组),空间复杂度是O(1)(总共维护两个变量)
算法的思想是,如果一个数存在超过数组的一半,则这个数和其他数相互抵消,最终还是会有自己的同党剩下来。
例如有一些人来投票,投的票选项分别是[1,3,2,1,1].
第一个人投1号,第二个人投3号,两个选票不同,相互抵消;
第三个人投2号,第四个人投1号,两个选票不同,相互抵消;
最后剩下一个1号投票,1号也是超过半数的投票号。
再看一个例子:[1,1,3,4,2]
第1、2个人的投票将选项1的票数累加到2;
第3、4个人的投票不是1,所以抵消选项1的票数到0;
最后一个人的投票是2.
但是很明显,2号不是多数的票号。所以我们需要再遍历一次数组来判断2号票是不是多数票。这个就是多数投票算法的思想。
JAVA实现:
public int majorityVote(int nums[]) {
int candidate=0,count=0;
for(Integer num : nums) {
if(num==candidate) {
count++;
}else if(count==0) {
candidate=num;
count=1;
}else {
count--;
}
}
count=0;
for(Integer num : nums)
if(num==candidate) count++;
if(count>Math.ceil(nums.length/2)) return candidate;
return -1; // 表示没有超过半数的票
}
传进函数的参数nums数组表示选票的票号,candidate表示可能的多数票号,初始值就设为0,count可以粗略理解为多数票号超过了其他票号的票数有多少。
拓展:
leetcode 229中求的就是超过三分之一的选票。
可以轻易想到,超过数组三分之一长度的数最多有两个,不可能有三个。
例如[1,1,1,2,2,2,3,3,3]就不存在超过数组三分之一长度的数字
而数组[1,1,1,2,2,2,3,3]存在两个超过数组三分之一长度的数字1、2
做法类似.