超过半数的数字 | 豆包MarsCode AI刷题
本题使用了Boyer-Moore投票算法,很高效率的一个寻找多数元素的算法。但是要注意使用时必须注意目标数组必须包含多数元素,因为其并不严格返回多数元素。
摘要
本文介绍了如何在一个数组中找到出现次数超过总数一半的数字。通过Boyer-Moore投票算法,我们可以在 时间内找到这个“多数元素”。该算法逻辑简单,且空间复杂度为 ,非常适合这种寻找多数元素的问题。文中包含Python和Go的实现代码。
问题描述
在一个包含多个数字的数组中,某个数字的出现次数超过了数组长度的一半。目标是找到这个“多数元素”。
示例
-
输入:
array = [1, 3, 8, 2, 3, 1, 3, 3, 3]
输出:3 -
输入:
array = [5, 5, 5, 1, 2, 5, 5]
输出:5 -
输入:
array = [9, 9, 9, 8, 9, 8, 8, 8, 8]
输出:9
原理分析
1. 投票算法的基本思想
投票算法的核心思想是:
- 如果我们把“多数元素”看作一个候选人,其余元素看作反对者,最终多数元素会“赢得选举”。
- 具体来说,假设在数组中,每个元素都可以投票支持一个候选人。
- 如果是多数元素,它会得到更多的投票,因为它的出现次数超过了一半。
- 如果不是多数元素,那么它会被其他不同的元素抵消。
2. 算法步骤
Boyer-Moore 投票算法的流程如下:
- 初始化:设置一个候选元素
candidate和计数器count,初始值分别为0和0。 - 遍历数组:对于数组中的每个元素,按照以下规则更新
candidate和count:- 如果
count为0,说明当前没有候选元素,或者当前的候选元素已经被抵消完了。在这种情况下,将当前的元素设置为新的候选元素,并将count设为1。 - 如果当前元素等于
candidate,说明它支持当前的候选元素,因此增加count。 - 如果当前元素不等于
candidate,说明它不支持当前的候选元素,因此减少count。
- 如果
- 最终结果:在遍历完数组后,
candidate就是数组中的多数元素。
3. 算法的正确性证明
- 由于题目保证多数元素的出现次数超过一半,所以当
count被抵消到0时,即使当前候选元素被更换成了新的元素,多数元素依然会“支持”新的候选元素,最终导致candidate会稳定在多数元素上。 - 当遍历完成时,
candidate一定是出现次数超过一半的元素。
4. 时间复杂度和空间复杂度
- 时间复杂度:O(n),只需要遍历数组一遍。
- 空间复杂度:O(1),只需要常数级别的额外空间。
5. 示例演示
示例 1
假设数组为 [1, 3, 8, 2, 3, 1, 3, 3, 3]
我们来一步步应用 Boyer-Moore 投票算法:
| 步骤 | 当前元素 | 候选元素 (candidate) | 计数器 (count) | 说明 |
|---|---|---|---|---|
| 初始 | 0 | 0 | 初始状态,候选元素和计数器均为 0 | |
| 1 | 1 | 1 | 1 | count 为 0,将 1 设为候选元素,count 为 1 |
| 2 | 3 | 1 | 0 | 3 不等于候选元素 1,count 减 1 为 0 |
| 3 | 8 | 8 | 1 | count 为 0,将 8 设为候选元素,count 为 1 |
| 4 | 2 | 8 | 0 | 2 不等于候选元素 8,count 减 1 为 0 |
| 5 | 3 | 3 | 1 | count 为 0,将 3 设为候选元素,count 为 1 |
| 6 | 1 | 3 | 0 | 1 不等于候选元素 3,count 减 1 为 0 |
| 7 | 3 | 3 | 1 | 3 等于候选元素 3,count 增 1 为 1 |
| 8 | 3 | 3 | 2 | 3 等于候选元素 3,count 增 1 为 2 |
| 9 | 3 | 3 | 3 | 3 等于候选元素 3,count 增 1 为 3 |
最终,candidate 为 3,即多数元素是 3。
为什么 Boyer-Moore 投票算法有效?
- 每次
count减少到0时,表示当前的候选元素已经“被击败”,需要重新选择一个新的候选元素。 - 由于多数元素的数量超过一半,最终它会占据主导地位,即使遇到不同的元素会暂时减少它的
count,但在算法的最后阶段,多数元素仍然会留下来作为最终的候选。
Boyer-Moore 投票算法的应用条件
- 已知数组中一定存在多数元素:Boyer-Moore 投票算法适用于这种情况。如果数组中没有多数元素,算法最后得到的
candidate并不一定满足题意。 - 要求在 O(n) 时间内解决:Boyer-Moore 投票算法非常高效,只需要遍历数组一次。
代码实现
Python代码
from typing import List
def solution(array: List[int]) -> int:
"""
该函数接收一个整数数组 array,并返回其中的“候选元素”。
使用的是“Boyer-Moore 投票算法”来找到数组中出现次数超过一半的元素(如果存在)。
"""
count = 0
candidate = array[0]
for num in array:
if count == 0:
# 如果计数为 0,则更新候选元素为当前元素
candidate = num
count = 1
elif candidate == num:
# 如果当前元素等于候选元素,增加计数
count += 1
else:
# 如果当前元素不等于候选元素,减少计数
count -= 1
return candidate
if __name__ == "__main__":
# 测试用例
print(solution([1, 3, 8, 2, 3, 1, 3, 3, 3]) == 3) # 应输出 True
print(solution([5, 5, 5, 1, 2, 5, 5]) == 5) # 应输出 True
print(solution([9, 9, 9, 8, 9, 8, 8, 8, 8]) == 9) # 应输出 True
Go语言代码
package main
import "fmt"
func solution(array []int) int {
count := 0
candidate := array[0]
for _, num := range array {
if count == 0 {
// 重新设定候选元素
candidate = num
count = 1
} else if candidate == num {
// 增加计数
count++
} else {
// 减少计数
count--
}
}
return candidate
}
func main() {
// 测试用例
fmt.Println(solution([]int{1, 3, 8, 2, 3, 1, 3, 3, 3}) == 3) // 应输出 true
fmt.Println(solution([]int{5, 5, 5, 1, 2, 5, 5}) == 5) // 应输出 true
fmt.Println(solution([]int{9, 9, 9, 8, 9, 8, 8, 8, 8}) == 9) // 应输出 true
}