🗳️ LeetCode 169:多数元素 —— 三种解法 & Boyer–Moore 投票算法的本质理解
一道看似简单的数组题,背后却藏着非常优雅的算法思想。
LeetCode 169:多数元素(Majority Element)
题目要求很简单:
给定一个大小为
n的数组nums,返回其中出现次数 大于 ⌊n / 2⌋ 的元素。
⚠️ 题目保证:多数元素一定存在。
一、先看问题的“数学结构”
如果某个元素出现次数 > n / 2,那意味着:
它的数量 > 所有其他元素数量之和
这条性质,正是后面所有解法的根基。
二、解法一:排序法(最直觉、最稳妥)
💡 思路
- 将数组排序
- 出现次数超过一半的元素,一定会占据 中位数位置
class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length / 2];
}
}
⏱️ 复杂度分析
- 时间复杂度:O(n log n)
- 空间复杂度:O(1) (忽略排序栈空间)
✅ 优点
- 思路极其简单
- 不容易写错
- 面试中“兜底解法”
❌ 缺点
- 没有利用「多数元素 > n/2」这个强约束
- 排序属于杀鸡用牛刀
👉 能用,但不优雅
三、解法二:HashMap 计数法(通用但偏重)
💡 思路
- 使用
HashMap统计每个数字出现的次数 - 找到 count > n / 2 的元素
class Solution {
public int majorityElement(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
int majority = 0, maxCount = 0;
for (int num : nums) {
int count = map.getOrDefault(num, 0) + 1;
map.put(num, count);
if (count > maxCount) {
maxCount = count;
majority = num;
}
}
return majority;
}
}
⏱️ 复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(n)
✅ 优点
- 通用性极强
- 好理解、好调试
- 可扩展到任意频率统计问题
❌ 缺点
- 使用了额外空间
- 统计了大量其实不必要的信息
👉 正确但“信息冗余”
四、解法三:Boyer–Moore 投票算法(本题最优解 ⭐)
💡 核心思想(一句话版)
多数元素的数量,大于所有其他元素之和。
把不同的元素两两抵消,最后剩下的一定是多数元素。
🧠 算法直觉解释
我们维护两个变量:
candidate:当前候选人count:候选人的“净票数”
规则只有三条:
count == 0→ 更换候选人- 当前元素 == 候选人 →
count + 1 - 当前元素 ≠ 候选人 →
count - 1
class Solution {
public int majorityElement(int[] nums) {
int candidate = 0;
int count = 0;
for (int num : nums) {
if (count == 0) {
candidate = num;
}
count += (num == candidate) ? 1 : -1;
}
return candidate;
}
}
🔍 为什么它一定正确?
把数组元素分成两类:
- M:多数元素
- ¬M:所有非多数元素
每一次 count--,都等价于:
删除一个 M 和一个 ¬M
但由于:
M 的数量 > ¬M 的数量
所以:
- 非多数元素会被先抵消完
- 多数元素一定至少剩 1 个
- 最终留下的 candidate 就是答案
👉 算法的本质是:数量差的不变量
⏱️ 复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(1)
✅ 优点
- 时间、空间双最优
- 完全利用题目数学结构
- 非常优雅
❌ 注意点
- 必须保证多数元素存在
- 如果题目条件不满足,需要二次验证
五、三种方法对比总结
| 方法 | 时间复杂度 | 空间复杂度 | 特点 |
|---|---|---|---|
| 排序 | O(n log n) | O(1) | 简单直观 |
| HashMap | O(n) | O(n) | 通用但偏重 |
| Boyer–Moore | O(n) | O(1) | 最优、最优雅 |
六、写在最后:为什么 Boyer–Moore 值得反复品味
Boyer–Moore 并不是在“统计最多的元素”,而是在:
利用问题本身的数学约束,把无关信息一层层消掉。
它教会我们的不是一段代码,而是一种算法思想:
当某个元素的“优势不可逆”时,可以用抵消来找答案。