题目
给一个长度为 n 的数组,数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
数据范围:n≤50000,数组中元素的值 0≤val≤10000。
要求:空间复杂度:O(1),时间复杂度 O(n)。
输入描述:保证数组输入非空,且保证有解。
示例1
输入:[1,2,3,2,2,2,5,4,2]
返回值:2
示例2
输入:[3,3,3,3,2,2,2]
返回值:3
示例3
输入:[1]
返回值:1
为了让解法更具有通用性,可以考虑添加一个额外条件:数组中不一定存在出现次数超过一半的数字,如果不存在的话返回-1。
这也是在面试过程中,很容易被拓展的一个点。
思路1:哈希表
使用哈希表进行计数,如果发现某个元素数量超过总数一半,说明找到了答案。
代码1
import java.util.*;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
int n = array.length;
Map<Integer, Integer> map = new HashMap<>();
for(int x : array) {
//getOrDefault(key, defaultValue):根据key取得对应的value值,若key不存在,返回默认值defaultValue
map.put(x, map.getOrDefault(x, 0) + 1);
if(map.get(x) > n / 2) {
return x;
}
}
return -1;
}
}
- 时间复杂度:O(n)。
- 空间复杂度:O(n)。
思路2:摩尔投票
摩尔投票:
- 在集合中寻找可能存在的多数元素,这一元素在输入的序列重复出现并占到了序列元素的一半以上;
- 在第一遍遍历之后应该再进行一个遍历以统计第一次算法遍历的结果出现次数,确定其是否为众数;
- 如果一个序列中没有占到多数的元素,那么第一次的结果就可能是无效的随机元素。
换句话说,每次将两个不同的元素进行抵消,如果最后有元素剩余,则可能为元素个数大于总数一半的那个。
具体地,定义一个变量 x 来保存那个可能为主要元素的值,cnt 用来记录该值的出现次数。然后在遍历数组 nums 过程中执行如下逻辑:
- 如果 cnt 为 0:说明之前出现过的 x 已经被抵消完了,更新一下 x 为当前值,出现次数为 1 :
x = nums[i], cnt = 1; - 如果 cnt 不为 0:说明之前统计的 x 还没被抵消完,这是根据 nums[i] 与 x 是否相等进行计算即可:
cnt += nums[i] == x ? 1 : -1。
当处理完 nums 之后,得到了一个可能的主要元素。注意只是可能,因为在处理过程中只使用了 x 和 cnt 来记录,无法确定最后剩下的 x 是经过多次抵消后剩余的主要元素,还是只是不存在主要元素的数组中的无效随机元素。
因此需要再进行一次遍历,检查这个可能的主要元素 x 的出现次数是否超过总数一半。
代码2
import java.util.*;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
int n = array.length;
int x = -1, cnt = 0;
for(int i : array) {
if(cnt == 0) {
x = i;
cnt = 1;
} else {
cnt += x == i ? 1 : -1;
}
}
cnt = 0;
for(int i : array) {
if(x == i) {
cnt++;
}
}
return cnt > n / 2 ? x : -1;
}
}
- 时间复杂度:O(n)。
- 空间复杂度:O(1)。
文章介绍了两种解决数组中找出现次数超过一半数字的问题的方法:一是使用哈希表记录每个数字出现的次数,二是运用摩尔投票算法进行元素抵消。两种方法的时间复杂度均为O(n),但空间复杂度不同,哈希表方法为O(n),摩尔投票为O(1)。
510

被折叠的 条评论
为什么被折叠?



