数组中占比超过一半的元素称之为主要元素。给你一个 整数 数组,找出其中的主要元素。若没有,返回 -1 。请设计时间复杂度为 O(N) 、空间复杂度为 O(1) 的解决方案。
示例 1:
输入:[1,2,5,9,5,9,5,5,5]
输出:5
示例 2:
输入:[3,2]
输出:-1
示例 3:
输入:[2,2,1,1,1,2,2]
输出:2
题解:刚开始想的使用Map来存储,key存储元素,value存储元素出现的次数。但是题目中要求空间复杂度为O(1).
class Solution {
public int majorityElement(int[] nums) {
Map<Integer,Integer> map=new HashMap<Integer,Integer>();
for(int i=0;i<nums.length;i++){
map.put(nums[i],map.getOrDefault(nums[i],0)+1);
}
for (Map.Entry<Integer,Integer> entry:map.entrySet()) {
if(entry.getValue()>nums.length/2){
return entry.getKey();
}
}
return -1;
}
}
后来看了题解之后发现还有摩尔投票这种做法,思想也很简单。
摩尔投票:
思想:在集合中寻找可能存在的多数元素,这一元素在输入的序列重复出现并占到了序列元素的一半以上;在第一遍遍历之后应该再进行一个遍历以统计第一次算法遍历的结果出现次数,确定其是否为众数;如果一个序列中没有占到多数的元素,那么第一次的结果就可能是无效的随机元素。
换句话说每次将两个不同的元素进行抵消,如果最后有元素剩余,则可能为元素个数大于总数一半的那个。
具体的:定义一个变量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的出现次数是否超过总数的一半。
class Solution {
public int majorityElement(int[] nums) {
int n = nums.length;
int x = -1, cnt = 0;
for (int i : nums) {
if (cnt == 0) {
x = i;
cnt = 1;
} else {
cnt += x == i ? 1 : -1;
}
}
cnt = 0;
for (int i : nums) if (x == i) cnt++;
return cnt > n / 2 ? x : -1;
}
}