问题描述
小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
解题思路
排序做法
思路:
如果一个数列存在主元素,那么该主元素在排序后必然出现在索引 n/2 的位置。
sort(a, a + n);
cout << a[n / 2];
时间复杂度: O(nlogn)
桶计数做法
思路:
统计每个数字出现的次数,找到出现次数超过 n / 2 的元素。
时间复杂度: O(n),空间复杂度:O(m)。
代码实现:
from collections import defaultdict
def find_majority_bucket(nums):
count = defaultdict(int)
n = len(nums)
for num in nums:
count[num] += 1
if count[num] > n // 2:
return num
摩尔投票法
思路:
利用一个变量 candidate 保存当前候选主元素,另一个变量 count 记录其“票数”,通过一遍遍历找到可能的候选主元素,然后验证它是否确实是主元素。
算法步骤:
-
初始化
candidate和count。 -
遍历数组:
- 如果
count为 0,则将当前元素设为candidate,并将count设为 1。 - 如果当前元素等于
candidate,则count加 1。 - 如果当前元素不等于
candidate,则count减 1。
- 如果
-
验证
candidate是否是主元素。
时间复杂度: O(n),空间复杂度: O(1)。
代码实现
def solution(array):
# 初始化候选元素和计数器
candidate = None
count = 0
# 遍历数组,进行投票
for num in array:
if count == 0:
# 如果计数器为0,更新候选元素
candidate = num
count = 1
elif num == candidate:
# 如果当前元素与候选元素相同,计数器加1
count += 1
else:
# 如果当前元素与候选元素不同,计数器减1
count -= 1
return candidate
if __name__ == "__main__":
# 测试样例
print(solution([1, 3, 8, 2, 3, 1, 3, 3, 3]) == 3)
print(solution([5, 5, 5, 1, 2, 5, 5]) == 5)
print(solution([9, 9, 9, 9, 8, 9, 8, 8]) == 9)
关键点
- 摩尔投票算法:通过投票机制,高效地找到数组中出现次数超过一半的元素。
- 时间复杂度:O(n),遍历数组两次。
- 空间复杂度:O(1),只使用了常数级别的额外空间。
比较
| 方法 | 时间复杂度 | 空间复杂度 | 特点 |
|---|---|---|---|
| 排序做法 | O(nlogn) | O(1) | 简单直接,但时间复杂度稍高 |
| 桶计数做法 | O(n) | O(m) | 快速,但空间复杂度取决于数值范围 |
| 摩尔投票法 | O(n) | O(1) | 高效,适合数值范围较大的情况,需验证候选元素 |
总结
通过使用摩尔投票算法,我们可以在 O(n) 的时间复杂度和 O(1) 的空间复杂度内,高效地找到数组中出现次数超过一半的元素。这个算法非常适合解决这类问题,并且在实际应用中也非常实用。