问题描述
给定一个整型数组,其中某个数字的出现次数超过了数组长度的一半。我们的任务是找到这个数字。
摩尔投票法的基本思想
摩尔投票法的核心思想是通过抵消的方式找到数组中的多数元素。具体步骤如下:
-
初始化:
- 设置一个候选元素
candidate,初始值为None。 - 设置一个计数器
count,初始值为 0。
- 设置一个候选元素
-
第一遍遍历数组:
- 遍历数组中的每个元素。
- 如果
count为 0,将当前元素设为candidate,并将count设为 1。 - 如果当前元素等于
candidate,将count增加 1。 - 如果当前元素不等于
candidate,将count减少 1。
-
第二遍遍历数组:
- 遍历数组,统计
candidate的出现次数。 - 如果
candidate的出现次数超过数组长度的一半,返回candidate。 - 否则,返回
None(虽然题目保证一定存在这样的元素,但这一步是为了完整性)。
- 遍历数组,统计
详细步骤
第一遍遍历
假设数组为 [1, 3, 8, 2, 3, 1, 3, 3, 3],我们逐步执行第一遍遍历:
- 初始化:
candidate = None,count = 0 - 遍历第一个元素
1:count == 0,所以candidate = 1,count = 1
- 遍历第二个元素
3:3 != 1,所以count = 0
- 遍历第三个元素
8:count == 0,所以candidate = 8,count = 1
- 遍历第四个元素
2:2 != 8,所以count = 0
- 遍历第五个元素
3:count == 0,所以candidate = 3,count = 1
- 遍历第六个元素
1:1 != 3,所以count = 0
- 遍历第七个元素
3:count == 0,所以candidate = 3,count = 1
- 遍历第八个元素
3:3 == 3,所以count = 2
- 遍历第九个元素
3:3 == 3,所以count = 3
经过第一遍遍历,candidate = 3,count = 3。
第二遍遍历
接下来,我们验证 candidate 是否确实是多数元素:
- 初始化
count = 0 - 遍历数组,统计
3的出现次数:1不是3,count = 03是3,count = 18不是3,count = 12不是3,count = 13是3,count = 21不是3,count = 23是3,count = 33是3,count = 43是3,count = 5
经过第二遍遍历,count = 5,大于数组长度的一半(9 / 2 = 4.5),因此 3 是多数元素。
完整代码
def solution(array):
# Edit your code here
candidate = None
count = 0
# 第一遍遍历,找到候选元素
for num in array:
if count == 0:
candidate = num
count = 1
elif num == candidate:
count += 1
else:
count -= 1
# 第二遍遍历,验证候选元素是否确实是多数元素
count = 0
for num in array:
if num == candidate:
count += 1
if count > len(array) // 2:
return candidate
else:
return None # 在题目保证一定存在的情况下,这行实际上不会被执行
if __name__ == "__main__":
# Add your test cases here
print(solution([1, 3, 8, 2, 3, 1, 3, 3, 3]) == 3)
总结
摩尔投票法通过抵消的方式高效地找到了数组中的多数元素。第一遍遍历用于找到候选元素,第二遍遍历用于验证候选元素是否确实是多数元素。这种方法的时间复杂度为 O(n),空间复杂度为 O(1),非常适合解决这类问题。