题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2
限制:
1 <= 数组长度 <= 50000
自己的想法(比较low)
因为用Java写的,所以借助的Map容器,正好也巩固了下HashMap常用方法和遍历方式。看了官方解法后…发现无论从空间还是时间是上说,才发现又更简单的方法
class Solution {
public int majorityElement(int[] nums) {
if(nums.length == 1) return nums[0];
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i = 0; i < nums.length; i++){
if(map.containsKey(nums[i])){
map.put(nums[i], map.get(nums[i])+1);
}else{
map.put(nums[i], 1);
}
}
int res = 0;
for(Map.Entry<Integer, Integer> entry: map.entrySet()){
if(entry.getValue() > nums.length/2){
res = entry.getKey();
break;
}
}
return res;
}
}
官方解法(摩尔投票法)
很巧妙,使用投票法来解决
摩尔投票法:
票数和: 由于众数出现的次数超过数组长度的一半;若记 众数 的票数为 +1 ,非众数 的票数为 -1 ,则一定有所有数字的 票数和 >0 。
票数正负抵消: 设数组 nums 中的众数为 x ,数组长度为 n 。若 nums 的前 a 个数字的 票数和 = 0 ,则 数组后 (n−a) 个数字的 票数和一定仍>0 (即后 (n−a) 个数字的 众数仍为 x )
算法原理
为构建正负抵消,假设数组首个元素 n1为众数,遍历统计票数,当发生正负抵消时,剩余数组的众数一定不变 ,这是因为(设真正的众数为 x )
- 当 n1=x 时: 抵消的所有数字中,有一半是众数 x 。
- 当 n1!=x时: 抵消的所有数字中,少于或等于一半是众数 xx
利用此特性,每轮假设都可以 缩小剩余数组区间 。当遍历完成时,最后一轮假设的数字即为众数(由于众数超过一半,最后一轮的票数和必为正数)
class Solution {
public int majorityElement(int[] nums) {
int candidate = nums[0];
int count = 1;
for (int i = 1;i < nums.length;i ++) {
if (nums[i] == candidate) {
count++;
}
else {
if (count == 0) {
candidate = nums[i];
}
else {
count--;
}
}
}
return candidate;
}
}
收获到了:
- 摩尔投票法。票数和: 由于众数出现的次数超过数组长度的一半。
- HashMap基本用法及遍历方式