LeetCode 169:多数元素 - 摩尔投票法的精妙解法
标签:LeetCode 169, 多数元素, 摩尔投票法, Java算法, 数组处理
题目描述
给定一个大小为 n
的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊n/2⌋
的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:[3,2,3]
输出:3
示例 2:
输入:[2,2,1,1,1,2,2]
输出:2
问题分析
多数元素问题要求我们找出数组中出现次数超过一半的元素。最直观的解法是使用哈希表统计元素出现次数,但这需要 O(n) 的额外空间。排序后取中间元素的解法需要 O(n log n) 的时间复杂度。那么是否存在一种既高效又节省空间的解法呢?
摩尔投票法 (Boyer-Moore Voting Algorithm) 正是解决这类问题的绝佳方案,它能在 O(n) 时间复杂度和 O(1) 空间复杂度内解决问题。
摩尔投票法原理
摩尔投票法的核心思想是元素抵消,它基于一个关键事实:由于多数元素的数量超过所有其他元素数量的总和,因此通过相互抵消的方式,最终剩下的必定是多数元素。
算法步骤
- 初始化候选元素
candidate
和计数器count
- 遍历数组中的每个元素:
- 当
count == 0
时,将当前元素设为候选元素 - 当当前元素等于候选元素时,
count++
- 当当前元素不等于候选元素时,
count--
- 当
- 返回候选元素作为多数元素
为什么有效?
假设多数元素为 m
,出现次数为 k
,数组长度为 n
,则:
k > n/2
- 其他元素总数为
n-k < k
在遍历过程中:
- 每个
m
使count+1
- 每个非
m
使count-1
最终计数相当于:count = k - (n - k) = 2k - n
因为 k > n/2
,所以 2k > n
,即 2k - n > 0
,因此最终 count > 0
,候选元素必定是多数元素。
Java代码实现
class Solution {public int majorityElement(int[] nums) {// 初始化候选元素和计数器int candidate = nums[0];int count = 0;for (int num : nums) {// 当计数器为0时,选择新的候选元素if (count == 0) {candidate = num;}// 更新计数器:当前元素等于候选元素则+1,否则-1count += (num == candidate) ? 1 : -1;}// 最终候选元素即为多数元素return candidate;}
}
算法演示
以数组 [2,2,1,1,1,2,2]
为例:
步骤 | 当前元素 | candidate | count | 说明 |
---|---|---|---|---|
1 | 2 | 2 | 1 | 初始化 candidate 为 2 |
2 | 2 | 2 | 2 | 相同元素,count++ |
3 | 1 | 2 | 1 | 不同元素,count– |
4 | 1 | 2 | 0 | 不同元素,count– |
5 | 1 | 1 | 1 | count=0,重置 candidate |
6 | 2 | 1 | 0 | 不同元素,count– |
7 | 2 | 2 | 1 | count=0,重置 candidate |
最终结果:2(正确)
复杂度分析
- 时间复杂度:O(n),只需遍历数组一次
- 空间复杂度:O(1),仅使用常数级别的额外空间
算法优势
- 空间效率:不需要额外的哈希表存储空间
- 时间效率:仅需一次遍历即可得到结果
- 简洁性:代码实现简单明了,逻辑清晰
- 通用性:适用于任何满足多数元素定义的场景
边界情况处理
虽然题目保证存在多数元素,但在实际应用中,我们可以添加验证步骤:
// 验证候选元素是否确实是多数元素
int verify = 0;
for (int num : nums) {if (num == candidate) {verify++;}
}if (verify > nums.length / 2) {return candidate;
} else {throw new IllegalArgumentException("No majority element exists");
}
实际应用场景
摩尔投票法不仅适用于算法题目,在实际开发中也有广泛应用:
- 选举系统中的多数票统计
- 分布式系统中的主节点选举
- 数据分析中的高频元素检测
- 容错系统中的多数决策机制
总结
摩尔投票法以其简洁高效的特点,成为解决多数元素问题的最佳方案。它通过巧妙的抵消策略,在O(n)时间复杂度和O(1)空间复杂度内解决问题,完美体现了"用简单方法解决复杂问题"的算法设计思想。
掌握摩尔投票法不仅能帮助你在算法面试中脱颖而出,更能提升你对高效算法设计的理解和应用能力。在实际开发中,这种空间高效的算法思想尤其适合处理大规模数据场景。
思考题:如果要求找出出现次数超过 n/3 的元素,该如何扩展摩尔投票法呢?