题目描述
小R从班级中抽取了一些同学,每位同学都会给出一个数字。已知在这些数字中,某个数字的出现次数超过了数字总数的一半。现在需要你帮助小R找到这个数字。
例如,在数组 [1, 3, 8, 2, 3, 1, 3, 3, 3] 中,数字 3 出现了5次,超过了数组长度9的一半,因此答案是 3。
解题思路
这个问题可以通过多种方法解决,但最高效的方法是使用 Boyer-Moore 投票算法。这个算法的时间复杂度为 O(n),空间复杂度为 O(1),非常适合解决这类问题。
Boyer-Moore 投票算法的核心思想
-
候选者选择:遍历数组,维护一个候选者
candidate和一个计数器count。初始时,将第一个元素设为候选者,计数器设为1。 -
计数器更新:
- 如果计数器为0,重新选择当前元素为新的候选者,并将计数器设为1。
- 如果当前元素等于候选者,计数器加1。
- 如果当前元素不等于候选者,计数器减1。
-
验证候选者:遍历数组,统计候选者的出现次数。如果候选者的出现次数超过数组长度的一半,则返回候选者;否则返回错误信息。
详细步骤
-
初始化:
candidate:初始设为数组的第一个元素。count:初始设为1。
-
第一次遍历数组:
- 遍历数组的每个元素。
- 如果
count为0,重新选择当前元素为新的候选者,并将count设为1。 - 如果当前元素等于
candidate,将count加1。 - 如果当前元素不等于
candidate,将count减1。
-
第二次遍历数组:
- 遍历数组,统计
candidate的出现次数。 - 如果
candidate的出现次数超过数组长度的一半,返回candidate。
- 遍历数组,统计
代码解析
def solution(array):
# 初始化候选者和计数器
candidate = array[0]
count = 1
# 第一次遍历数组,选择候选者
for i in range(1, len(array)):
if count == 0:
candidate = array[i]
count = 1
elif array[i] == candidate:
count += 1
else:
count -= 1
# 第二次遍历数组,验证候选者
count = 0
for i in range(len(array)):
if array[i] == candidate:
count += 1
# 返回候选者(题目保证一定存在这样的数字)
return candidate
输入输出全过程分析实例
示例 1
输入数组:[1, 3, 8, 2, 3, 1, 3, 3, 3]
-
初始化:
candidate = 1count = 1
-
第一次遍历数组:
i = 1,array[1] = 3,count = 0,重新选择candidate = 3,count = 1i = 2,array[2] = 8,count = 0,重新选择candidate = 8,count = 1i = 3,array[3] = 2,count = 0,重新选择candidate = 2,count = 1i = 4,array[4] = 3,count = 0,重新选择candidate = 3,count = 1i = 5,array[5] = 1,count = 0,重新选择candidate = 1,count = 1i = 6,array[6] = 3,count = 0,重新选择candidate = 3,count = 1i = 7,array[7] = 3,count = 2i = 8,array[8] = 3,count = 3
-
第二次遍历数组:
- 统计
candidate = 3的出现次数,结果为5。
- 统计
-
返回结果:
return 3
输出:3
示例 2
输入数组:[2, 2, 1, 1, 1, 2, 2]
-
初始化:
candidate = 2count = 1
-
第一次遍历数组:
i = 1,array[1] = 2,count = 2i = 2,array[2] = 1,count = 1i = 3,array[3] = 1,count = 0,重新选择candidate = 1,count = 1i = 4,array[4] = 1,count = 2i = 5,array[5] = 2,count = 1i = 6,array[6] = 2,count = 0,重新选择candidate = 2,count = 1
-
第二次遍历数组:
- 统计
candidate = 2的出现次数,结果为4。
- 统计
-
返回结果:
return 2
输出:2
学习心得
- 算法选择的重要性:Boyer-Moore 投票算法是一个非常高效的算法,适用于寻找数组中出现次数超过一半的数字。它的时间复杂度为 O(n),空间复杂度为 O(1),非常适合处理大数据量的情况。
- 问题分解:将问题分解为候选者选择和验证两个步骤,可以使算法更加清晰和高效。
- 边界条件处理:虽然题目保证一定存在一个出现次数超过一半的数字,但在实际应用中,处理边界条件是非常重要的,可以增加代码的健壮性。