LEETCODE 169Easy
多数元素
摩尔投票把“统计谁最多”变成“互相抵消”,多数元素靠数量优势一定活到最后。
问题拆解
多数元素是出现次数 严格大于 ⌊n/2⌋ 的元素,题目保证它一定存在。
最直接的想法是用哈希表统计每个元素出现多少次,再挑出次数过半的那个。这样思路清晰,但要额外花 O(n) 的空间。进阶要求把空间压到 O(1),就得利用“过半”这个很强的条件,而不只是数数。
多数元素比其他所有元素加起来还多,如果让不同的元素两两抵消,最后剩下的必然是它。
哈希计数(作为对照)
public int majorityElement(int[] nums) {
Map<Integer, Integer> count = new HashMap<>();
for (int x : nums) {
int c = count.merge(x, 1, Integer::sum);
if (c > nums.length / 2) {
return x;
}
}
return -1; // 题目保证多数元素存在,走不到这里
}
def majorityElement(nums):
count = {}
for x in nums:
count[x] = count.get(x, 0) + 1
if count[x] > len(nums) // 2:
return x
func majorityElement(nums []int) int {
count := make(map[int]int)
for _, x := range nums {
count[x]++
if count[x] > len(nums)/2 {
return x
}
}
return -1 // 题目保证多数元素存在,走不到这里
}
pub fn majority_element(nums: Vec<i32>) -> i32 {
let mut count = std::collections::HashMap::new();
for &x in &nums {
let c = count.entry(x).or_insert(0);
*c += 1;
if *c > nums.len() / 2 {
return x;
}
}
unreachable!() // 题目保证多数元素存在
}
这版本一眼就对,适合先确认题意。它的问题只在空间:最坏情况下要为几乎所有不同元素各记一笔。
摩尔投票
维护一个候选人 candidate 和一张票数 count。遍历时,票数为 0 就把当前元素立为新候选人;之后遇到相同元素票数加一,遇到不同元素票数减一——相当于一张反对票抵消一张支持票。
public int majorityElement(int[] nums) {
int count = 0;
int candidate = 0;
for (int x : nums) {
if (count == 0) {
candidate = x;
}
count += x == candidate ? 1 : -1;
}
return candidate;
}
def majorityElement(nums):
count = 0
candidate = None
for x in nums:
if count == 0:
candidate = x
count += 1 if x == candidate else -1
return candidate
func majorityElement(nums []int) int {
count, candidate := 0, 0
for _, x := range nums {
if count == 0 {
candidate = x
}
if x == candidate {
count++
} else {
count--
}
}
return candidate
}
pub fn majority_element(nums: Vec<i32>) -> i32 {
let mut count = 0;
let mut candidate = 0;
for x in nums {
if count == 0 {
candidate = x;
}
count += if x == candidate { 1 } else { -1 };
}
candidate
}
为什么最后剩下的一定是多数元素?把每个非多数元素看成消耗掉一张多数元素的票。多数元素的票数超过一半,其余所有元素加起来也不够把它的票全部抵消,所以无论顺序如何打乱,它的净票数始终为正,最终稳稳占据 candidate。
因为题目保证多数元素存在,这里不需要第二遍验证;如果不保证,还得再扫一遍确认候选人真的过半。
复杂度
| 指标 | 复杂度 | 原因 |
|---|---|---|
| 时间 | O(n) |
一次遍历完成抵消 |
| 空间 | O(1) |
只保存候选人和票数 |
可以迁移的模式
- 存在一个“数量占绝对优势”的元素;
- 允许用抵消、配对来消去噪声,而不是精确计数;
- 目标是把空间从
O(n)压到常数。
摩尔投票还能推广到“出现次数超过 ⌊n/3⌋”,那时维护两个候选人即可。