问题描述
小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
这道题目要求我们找到一个数字,这个数字在给定的数组中出现的次数超过了数组总数的一半。也就是说,题目中的数字被称为 "多数元素" (Majority Element)。多数元素的出现次数大于数组元素总数的一半,因此它一定是唯一的。
题目分析
给定一个整数数组,数组中的某个数字出现的次数超过数组总数的一半,任务是找出这个数字。
- 输入:一个整数数组
array。 - 输出:返回数组中出现次数超过一半的数字。
解法分析
这道题目可以通过 Boyer-Moore 投票算法 来高效解决。这个算法的核心思想是,通过在遍历数组时维护一个候选数字和一个计数器,最终确定出现次数超过一半的数字。
Boyer-Moore 投票算法
核心思想:
- 选择候选数字:遍历数组时,我们不断更新候选数字。如果当前数字与候选数字相同,计数器加一;如果不同,计数器减一。如果计数器变为零,我们选择当前数字作为新的候选数字。
- 验证候选数字:由于题目保证有一个数字出现的次数超过数组总数的一半,所以在第一轮遍历后,我们得到的候选数字必定是多数元素。因此,我们只需要再次遍历数组,统计该数字的出现次数,验证它是否真的满足题目要求。
具体实现
public class Main {
public static int solution(int[] array) {
int count = 0; // 计数器
Integer candidate = null; // 候选数字
// 第一遍遍历,找出候选数字
for (int num : array) {
if (count == 0) {
candidate = num; // 选择一个新的候选数字
}
count += (num == candidate) ? 1 : -1; // 如果当前数字与候选相同,计数器加1,否则减1
}
// 第二遍遍历,验证候选数字是否确实是多数元素
count = 0; // 重新计数
for (int num : array) {
if (num == candidate) {
count++; // 统计候选数字出现的次数
}
}
// 判断候选数字出现的次数是否超过数组长度的一半
if (count > array.length / 2) {
return candidate; // 如果是多数元素,返回该数字
} else {
return -1; // 如果没有多数元素,返回-1
}
}
public static void main(String[] args) {
// 测试样例
System.out.println(solution(new int[]{1, 3, 8, 2, 3, 1, 3, 3, 3}) == 3); // 输出3
System.out.println(solution(new int[]{5, 5, 5, 1, 2, 5, 5}) == 5); // 输出5
System.out.println(solution(new int[]{9, 9, 9, 9, 8, 9, 8, 8}) == 9); // 输出9
}
}
代码解释
-
候选数字的选择:
- 我们使用
count来记录当前候选数字的计数。如果count == 0,说明当前没有有效的候选数字,就将当前数字设置为新的候选数字。 - 对于每一个数字,如果它与候选数字相同,
count加一;如果不同,count减一。如果count为零,则需要重新选择候选数字。
- 我们使用
-
验证候选数字:
- 一旦遍历完成,我们得到一个候选数字。接着,再次遍历数组统计该候选数字出现的次数,验证它是否真的出现超过数组长度的一半。如果是,则返回该数字;否则返回
-1。
- 一旦遍历完成,我们得到一个候选数字。接着,再次遍历数组统计该候选数字出现的次数,验证它是否真的出现超过数组长度的一半。如果是,则返回该数字;否则返回
算法分析
- 时间复杂度:
O(n)。我们需要遍历数组两次,第一遍找到候选数字,第二遍验证候选数字的出现次数。 - 空间复杂度:
O(1)。只使用了常数空间来存储计数器和候选数字,因此空间复杂度是常数级别。
优点
- 效率高:该算法只需要遍历数组两次,时间复杂度为 O(n),适合处理大规模数据。
- 空间节省:只需要常数级别的额外空间,节省了内存。
总结
这道题的关键在于理解 Boyer-Moore 投票算法,它通过巧妙地维护一个候选数字和计数器,在遍历数组时可以高效地找出多数元素。这种方法不仅时间复杂度低,而且空间复杂度也极低,是解决这类问题的经典算法。