问题描述
小R从班级中抽取了一些同学,每位同学都会给出一个数字。已知在这些数字中,某个数字的出现次数超过了数字总数的一半。现在需要你帮助小R找到这个数字。
测试样例
样例1:
输入:array = [1, 3, 8, 2, 3, 1, 3, 3, 3]
输出:3
样例2:
输入:array = [5, 5, 5, 1, 2, 5, 5]
输出:5
样例3:
输入:array = [9, 9, 9, 9, 8, 9, 8, 8]
输出:9
算法思路
摩尔投票算法是一种用于在数组中找到出现次数超过一半的元素的高效算法。
摩尔投票算法的原理
候选元素的选择:
- 算法的核心思想是通过不断抵消不同的元素来找到出现次数超过一半的元素。
- 初始时,选择数组中的第一个元素作为候选元素
candidate,并设置计数器count为1。
遍历数组:
-
遍历数组中的每个元素:
-
如果当前元素与
candidate相同,则增加count。 -
如果当前元素与
candidate不同,则减少count。 -
如果
count变为零,则更新candidate为当前元素,并将count重置为1。
最终候选元素:
- 遍历结束后,
candidate就是出现次数超过一半的元素。 - 这是因为出现次数超过一半的元素在抵消过程中会始终保持优势,最终成为
candidate。
为什么这个算法有效?
-
抵消机制:
- 假设数组中有一个元素
x出现次数超过一半。 - 在遍历过程中,每次遇到一个与
x不同的元素,count就会减少1。 - 由于
x的出现次数超过一半,最终x会抵消掉所有其他元素,使得count仍然大于零。
- 假设数组中有一个元素
-
计数器重置:
- 当
count变为零时,说明当前的candidate已经被抵消掉了。 - 此时,选择一个新的
candidate并重置count,继续进行抵消过程。
- 当
摩尔投票算法通过抵消不同的元素来找到出现次数超过一半的元素,时间复杂度为 O(n),空间复杂度为 O(1)。
算法步骤
-
初始化一个候选数字
candidate和计数器count。 -
遍历数组中的每个数字:
- 如果
count为零,将当前数字设为candidate,并将count设为1。 - 如果当前数字与
candidate相同,增加count。 - 如果当前数字与
candidate不同,减少count。
- 如果
-
最终,
candidate就是我们要找的数字。
代码运行过程(举例)
输入:array = [1, 3, 8, 2, 3, 1, 3, 3, 3] 输出:3
-
初始化:
candidate初始化为0。count初始化为0。
-
遍历数组:
-
第一个元素
1:count为0,所以更新candidate为1,count为1。- 当前状态:
candidate = 1,count = 1。
-
第二个元素
3:3不等于candidate(即1),所以count减1,count变为0。- 当前状态:
candidate = 1,count = 0。
-
第三个元素
8:count为0,所以更新candidate为8,count为1。- 当前状态:
candidate = 8,count = 1。
-
第四个元素
2:2不等于candidate(即8),所以count减1,count变为0。- 当前状态:
candidate = 8,count = 0。
-
第五个元素
3:count为0,所以更新candidate为3,count为1。- 当前状态:
candidate = 3,count = 1。
-
第六个元素
1:1不等于candidate(即3),所以count减1,count变为0。- 当前状态:
candidate = 3,count = 0。
-
第七个元素
3:count为0,所以更新candidate为3,count为1。- 当前状态:
candidate = 3,count = 1。
-
第八个元素
3:3等于candidate(即3),所以count加1,count变为2。- 当前状态:
candidate = 3,count = 2。
-
第九个元素
3:3等于candidate(即3),所以count加1,count变为3。- 当前状态:
candidate = 3,count = 3。
-
-
返回结果:
- 遍历结束后,
candidate为3,count为3。 - 最终返回
candidate,即3。
- 遍历结束后,
Solution
public class Main {
public static int solution(int[] array) {
// 初始化候选数字和计数器
int candidate = 0;
int count = 0;
// 遍历数组
for (int num : array) {
// 如果计数器为零,更新候选数字
if (count == 0) {
candidate = num;
count = 1;
} else if (num == candidate) {
// 如果当前数字与候选数字相同,增加计数器
count++;
} else {
// 如果当前数字与候选数字不同,减少计数器
count--;
}
}
// 返回候选数字
return candidate;
}
public static void main(String[] args) {
// 添加你的测试用例
System.out.println(solution(new int[]{1, 3, 8, 2, 3, 1, 3, 3, 3}) == 3);
System.out.println(solution(new int[]{5, 5, 5, 1, 2, 5, 5}) == 5);
System.out.println(solution(new int[]{9, 9, 9, 9, 8, 9, 8, 8}) == 9);
}
}