题目描述
这是 LeetCode 上的 面试题 17.10. 主要元素 ,难度为 简单。
Tag : 「哈希表」、「摩尔投票」
数组中占比超过一半的元素称之为主要元素。给你一个 整数 数组,找出其中的主要元素。
若没有,返回 -1 。请设计时间复杂度为 O(N)O(N) 、空间复杂度为 O(1)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 复制代码
哈希表
一个朴素的做法是使用哈希表进行计数,如果发现某个元素数量超过总数一半,说明找到了答案。
Java 代码:
class Solution { public int majorityElement(int[] nums) { int n = nums.length; Map<Integer, Integer> map = new HashMap<>(); for (int x : nums) { map.put(x, map.getOrDefault(x, 0) + 1); if (map.get(x) > n / 2) return x; } return -1; } } 复制代码
Python 3 代码:
class Solution: def majorityElement(self, nums: List[int]) -> int: n = len(nums) hashmap = defaultdict(int) for x in nums: hashmap[x] += 1 if hashmap[x] > n // 2: return x return -1 复制代码
- 时间复杂度:O(n)O(n)
- 空间复杂度:O(n)O(n)
摩尔投票
这还是道「摩尔投票」模板题。
摩尔投票 :在集合中寻找可能存在的多数元素,这一元素在输入的序列重复出现并占到了序列元素的一半以上;在第一遍遍历之后应该再进行一个遍历以统计第一次算法遍历的结果出现次数,确定其是否为众数;如果一个序列中没有占到多数的元素,那么第一次的结果就可能是无效的随机元素。
换句话说,每次将两个不同的元素进行「抵消」,如果最后有元素剩余,则「可能」为元素个数大于总数一半的那个。
具体的,我们定义一个变量 xx 来保存那个可能为主要元素的值,cntcnt 用来记录该值的出现次数。然后在遍历数组 numsnums 过程中执行如下逻辑:
- 如果 cntcnt 为 00:说明之前出现过的 xx 已经被抵消完了,更新一下 xx 为当前值,出现次数为 11:
x = nums[i], cnt = 1
; - 如果 cntcnt 不为 00:说明之前统计的 xx 还没被抵消完,这是根据 nums[i]nums[i] 与 xx 是否相等进行计算即可:
cnt += nums[i] == x ? 1 : -1
。
当处理完 numsnums 之后,我们得到了一个「可能」的主要元素。注意只是可能,因为我们在处理过程中只使用了 xx 和 cntcnt 来记录,我们是无法确定最后剩下的 xx 是经过多次抵消后剩余的主要元素,还是只是不存在主要元素的数组中的无效随机元素。
因此我们需要再进行一次遍历,检查这个「可能」的主要元素 xx 的出现次数是否超过总数一半。
Java 代码:
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; } } 复制代码
Python 3 代码:
class Solution: def majorityElement(self, nums: List[int]) -> int: n = len(nums) x, cnt = -1, 0 for i in nums: if not cnt: x = i if x == i: cnt += 1 else: cnt -= 1 return x if cnt and nums.count(x) > n // 2 else -1 复制代码
- 时间复杂度:O(n)O(n)
- 空间复杂度:O(1)O(1)
最后
这是我们「刷穿 LeetCode」系列文章的第 No.面试题 17.10
篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour… 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。