掘金团队号上线,助你 Offer 临门! 点击 查看详情
一、题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例:
输入
[1, 2, 3, 2, 2, 2, 5, 4, 2]
输出
2
限制
1 <= 数组长度 <= 50000
二、思路分析
我的思路
- 遍历数组,一个map存放数字的次数,累加次数大于数组一半的时候退出遍历。
- 数组排序法: 数组排序完后,中间的那个数必然是那个数。
最佳思路
- 摩尔投票法:时间和空间复杂度分别为 O(N) 和 O(1)
- 好好解释下这个摩尔投票法:
- 我的感觉就是多数派投票,那么多数的投票的数一定多。
- 所以我们默认第一个就是 多数派, 后面开始投票,后面的数和其相同 我们票数+1 不同 我们票数-1
- 当票数=0 时候,说明前面遍历完的 多数派=少数派。
- 既然多数派> 总数的一半,所以后面多数派也一定>少数派。
- 我们相当于,前面打平了,后面的数组重新开始。 第一个认为多数派,然后后面投票,为0时,再以第一个开始,直到遍历所有。那么当前指定的那个多数派,就是结果。
- 主要的思想就是:对拼消耗!!! 杀敌1千自损1千,最终剩下的就是胜利者
- 好好解释下这个摩尔投票法:
三、AC 代码
我的写法:
public int majorityElement(int[] nums) {
Map<Integer, Integer> numTimesMap = new HashMap<>();
for (int num : nums) {
Integer times = numTimesMap.get(num);
if (times == null) {
numTimesMap.put(num, 1);
} else {
if (times + 1 > nums.length / 2) {
return num;
}
numTimesMap.put(num, times + 1);
}
}
return nums[0];
}
最佳写法:
public int majorityElementBetter(int[] nums) {
int res = 0, votes = 0;
for (int num : nums) {
// 如果票数为0,那么从当前位置重新开始
if (votes == 0) {
res = num;
votes++;
} else {
// votes += 1 if num == x else -1 可以这么缩写,但是实际开发不推崇
if (res == num) {
votes++;
} else {
votes--;
}
}
}
return res;
}
四、总结
比较典型的套路题了,解题目的思路比价多,但是最佳的解法很经验,没刷过的话,不容易想出。