前言
在算法学习的道路上,实践是巩固知识、提升技能的最佳方式。参加了豆包MarsCode 技术训练营后,我有机会深入接触各种经典算法,并在实际问题中应用它们。今天,我想分享在训练营中遇到的一道经典算法题——寻找数组中出现次数超过一半的元素,以及背后的摩尔投票算法。
正文
问题描述
在训练营的一次编程练习中,我们遇到这样一个问题:
解题思路
初步想法
最直观的解法是使用哈希表统计每个元素出现的次数,然后找出出现次数超过 n/2 的元素。然而,这种方法需要 O(n) 的时间复杂度和 O(n) 的空间复杂度。在训练营的讨论中,我们被鼓励寻找更高效的算法。
摩尔投票算法
这时,我们可以询问豆包,介绍了摩尔投票算法(Boyer-Moore Voting Algorithm) 。它能够在 O(n) 的时间内、使用 O(1) 的空间,找到数组中出现次数超过一半的元素。
算法思想:
-
候选人选择: 假设第一个元素为候选人,计数器初始化为1。
-
计数器更新:
- 如果遍历到的元素与候选人相同,计数器加1。
- 如果不同,计数器减1。
-
候选人更替:
- 当计数器为0时,将当前元素设为新的候选人,计数器重置为1。
-
结果验证: 遍历结束后,验证候选人是否确实为多数元素。
为什么可行?
在数组中,超过一半的元素数量一定比其他元素的总和还多。通过抵消的方式,其他元素最终会被多数元素“淹没”,因此最后剩下的候选人就是我们要找的元素。
代码实现
在训练营的实战环节,我们用 Java 实现了这个算法:
public class Main {
public static int findMajorityElement(int[] array) {
int candidate = array[0];
int count = 1;
// 第一遍遍历,寻找候选人
for (int i = 1; i < array.length; i++) {
if (array[i] == candidate) {
count++;
} else {
count--;
if (count == 0) {
candidate = array[i];
count = 1;
}
}
}
// 第二遍遍历,验证候选人是否为多数元素
count = 0;
for (int num : array) {
if (num == candidate) {
count++;
}
}
if (count > array.length / 2) {
return candidate;
} else {
throw new IllegalArgumentException("不存在出现次数超过一半的元素");
}
}
public static void main(String[] args) {
int[] array = {1, 3, 8, 2, 3, 1, 3, 3, 3};
System.out.println("多数元素是:" + findMajorityElement(array));
}
}
代码解析
-
初始化候选人和计数器:
int candidate = array[0]; int count = 1;将数组的第一个元素设为候选人,计数器设为1。
-
遍历寻找候选人:
for (int i = 1; i < array.length; i++) { if (array[i] == candidate) { count++; } else { count--; if (count == 0) { candidate = array[i]; count = 1; } } }- 遇到相同元素,计数器加1。
- 遇到不同元素,计数器减1;若计数器为0,更新候选人。
-
验证候选人是否为多数元素:
count = 0; for (int num : array) { if (num == candidate) { count++; } } if (count > array.length / 2) { return candidate; } else { throw new IllegalArgumentException("不存在出现次数超过一半的元素"); }重新统计候选人出现的次数,确认其是否超过一半。
算法分析
- 时间复杂度: O(n) ,因为数组遍历了两遍。
- 空间复杂度: O(1) ,只使用了常数级别的额外空间。
训练营心得
在豆包MarsCode 技术训练营的学习中,我深刻体会到了算法优化的重要性。摩尔投票算法的巧妙之处在于利用了多数元素的性质,避免了额外的空间开销。
通过与其他学员的讨论,我也认识到在解决问题时,不能只停留在表面,而要深入思考问题的本质,寻找最优的解法。
思考
- 扩展思考: 如果数组中不存在出现次数超过一半的元素,如何修改算法来处理这种情况?
- 进阶挑战: 能否设计一个算法,找出数组中所有出现次数超过 n/3 的元素?
期待在训练营中与进一步交流和探讨。