题目地址:
https://www.lintcode.com/problem/majority-element/description
给定一个数组,其中有一个数字出现的次数超过数组长度的一半。求出它。
可以用经典的摩尔投票算法,也就是Boyer-Moore Voting Algorithm。思路是这样的,从左向右扫描数组,同时维护一个变量count和另一个变量res储存最终答案。遍历到第一个数时, 初始化为第一个数,再将count加 ;此后,只要遇到等于res的数,就计count加一(相当于对其投票),如果遇到不同的数,就将count减一,如果count减到了 ,再遇到不同的数的时候,就将res改为那个新数,然后count加一,重新开始投票。扫描完数组后res存储的就是最终答案。代码如下:
import java.util.List;
public class Solution {
/*
* @param nums: a list of integers
* @return: find a majority number
*/
public int majorityNumber(List<Integer> nums) {
// write your code here
int res = 0, count = 0;
for (int i = 0; i < nums.size(); i++) {
// 如果计数为0,就选取新候选数并开始投票
if (count == 0) {
res = nums.get(i);
count++;
} else {
// 如果遇到相等的数则投票+1,否则投票-1
count += nums.get(i) == res ? 1 : -1;
}
}
return res;
}
}
时间复杂度 ,空间 。
算法正确性证明:
构造一个数对序列,假设遍历到某数时
,当前res为
,如果
,那么就写下一个数对
,也就是说每当count减少时,就写下一个数对,这个数对数字次序可以交换。如果count增加的时候则不写。如此一来,当整个数组遍历完成之后,就可以写下不超过
个数对,其中
为数组长度。
首先证明,如果将所有数对里的数都从数组里删掉,那么数组剩下的数一定都相等。假如不然,那么存在两个数 使得这两个数不属于任何数对,不妨设 的位置在 左边,那么当遍历到 的时候,res里的数必然是 ,而在遍历到 时,res里的数必然是 ,因为只有这样 和 才不会出现在任何数对里;然而这是不可能发生的,因为在遍历到 之前, 对应的count必然已经清到 了,所以 必然属于某个数对。所以遍历完后不在数对里的数必然都相等。
接下来证明这些数就是出现次数大于一半的数 。由于数对的数量是小于等于 的,所以出现在数对里的 的个数必然小于等于 ,而 出现的总次数是大于 的,所以 一定会最后剩下,也就是res里会存储 。证毕。