问题描述
小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
def solution(inp): candidate = None count = 0
for num in inp:
if count == 0:
candidate = num
count += (1 if num == candidate else -1)
return candidate
if name == "main": # 添加测试用例 test1 = [1, 1, 2, 2, 3, 3, 4, 5, 5] test2 = [0, 1, 0, 1, 2]
print(solution(test1) == 4)
print(solution(test2) == 2)
问题理解
你需要找到一个在数组中出现次数超过一半的数字。这意味着这个数字的出现次数比其他所有数字的出现次数之和还要多。
数据结构选择
由于我们只需要找到一个数字,而不需要存储所有数字的出现次数,因此我们可以选择一个简单的数据结构来帮助我们解决问题。
算法步骤
摩尔投票算法:这是一种高效的算法,可以在 O(n) 时间和 O(1) 空间内解决问题。
初始化:设置一个候选数字 candidate 和计数器 count。
遍历数组:对于数组中的每个数字: 如果 count 为 0,将当前数字设为 candidate。 如果当前数字与 candidate 相同,count 加一。 如果当前数字与 candidate 不同,count 减一。
返回结果:最终的 candidate 就是出现次数超过一半的数字。
摩尔投票算法的优势 时间复杂度:O(n),只需要遍历一次数组。 空间复杂度:O(1),只需要常数级别的额外空间。
伪代码
plaintext function solution(array): candidate = None count = 0 for num in array: if count == 0: candidate = num if num == candidate: count += 1 else: count -= 1 return candidate
解释
初始化:candidate 和 count 初始化为 None 和 0。
遍历数组:对于每个数字 num:
如果 count 为 0,将 num 设为 candidate。 如果 num 与 candidate 相同,count 加一。 如果 num 与 candidate 不同,count 减一。
返回结果:最终的 candidate 就是出现次数超过一半的数字。
进一步思考
验证结果:虽然摩尔投票算法能够找到候选数字,但在某些情况下,可能需要再次遍历数组来验证这个数字的出现次数是否真的超过一半。
算法正确性证明
摩尔投票算法的正确性基于以下两个关键点:
抵消机制: 假设数组中存在一个出现次数超过一半的数字 x。在遍历数组的过程中,x 的出现次数会不断增加,而其他数字的出现次数会被抵消。
由于 x 的出现次数超过一半,因此在抵消过程中,x 的计数最终会大于 0,并且 x 会成为最终的 candidate。
唯一性:
如果数组中存在一个出现次数超过一半的数字 x,那么在遍历数组的过程中,x 的计数最终会大于 0,并且 x 会成为最终的 candidate。
如果数组中不存在出现次数超过一半的数字,那么最终的 candidate 可能是一个错误的候选数字。因此,在实际应用中,通常需要再次遍历数组来验证 candidate 的出现次数是否真的超过一半。
返回结果:
最终的 candidate 是 3,并且 3 的出现次数确实超过了一半。
总结 摩尔投票算法通过抵消不同的数字来找到最终的候选数字,并且由于出现次数超过一半的数字在抵消过程中会始终保持计数大于 0,因此最终的候选数字就是出现次数超过一半的数字。这个算法的时间复杂度为 O(n),空间复杂度为 O(1),非常高效。