跳到主要内容

0169. 多数元素

题目描述

给定一个大小为 n 的数组 nums,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例

示例 1:

输入:nums = [3,2,3]
输出:3

示例 2:

输入:nums = [2,2,1,1,1,2,2]
输出:2

解题思路

第一步:理解问题

要找出出现次数超过一半的元素。这意味着:如果把这个元素的所有出现和其他元素两两配对抵消,它最后一定会剩下。

第二步:暴力解法

用哈希表统计每个数字出现次数:

from collections import Counter

def majorityElement(nums):
cnt = Counter(nums)
for k, v in cnt.items():
if v > len(nums) // 2:
return k
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

第三步:排序解法

排序后,中间位置 n // 2 的元素一定是多数元素:

def majorityElement(nums):
nums.sort()
return nums[len(nums) // 2]
  • 时间复杂度:O(n log n)
  • 空间复杂度:O(1)O(log n)

第四步:最优解法 - Boyer-Moore 投票算法

核心理念:把不同的两两抵消,最后剩下的就是多数元素。

算法步骤:

  1. 维护候选者 candidate 和计数器 count
  2. 遍历数组:
    • count == 0 时,当前元素成为新的 candidate
    • 当前元素 == candidate 时,count += 1
    • 否则 count -= 1
  3. 最后 candidate 就是答案

为什么正确?因为多数元素出现次数 > n/2,即使和其他所有元素抵消,它仍会幸存。

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

完整代码实现

from typing import List

class Solution:
"""
169. 多数元素 - Boyer-Moore 投票算法

核心思想:
把不同的元素两两抵消,最后剩下的就是出现次数超过一半的多数元素。

时间复杂度:O(n)
空间复杂度:O(1)
"""

def majorityElement(self, nums: List[int]) -> int:
candidate = nums[0]
count = 0

for num in nums:
if count == 0:
candidate = num
if num == candidate:
count += 1
else:
count -= 1

return candidate

示例推演

nums = [2, 2, 1, 1, 1, 2, 2] 为例:

元素count 前candidatecount 后
2021
2122
1221
1120
1011
2110
2021

最终 candidate = 2

从抵消的角度理解:

  • 2, 2, 1, 1 互相抵消
  • 剩余 1, 2, 2
  • 1 和一个 2 抵消
  • 剩余 2

复杂度分析

解法时间复杂度空间复杂度说明
哈希表O(n)O(n)直观但空间占用大
排序O(n log n)O(1)不满足时间最优
投票算法O(n)O(1)最优解

易错点总结

1. count == 0 时才换 candidate

即使当前 num 不等于 candidate,只要 count > 0,只能说明这个 candidate 的优势被削弱了,还不能确定它不是多数元素。

2. 初始 candidate

可以设为 nums[0],也可以随便设一个值,效果一样,因为 count = 0 会立即被第一个元素覆盖。

3. 题目保证有解

如果题目不保证有解,投票算法结束后还需要再遍历一遍验证 candidate 的出现次数是否 > n/2。


扩展思考

如果要求找出所有出现次数 > n/3 的元素?

这是 229. 多数元素 II,需要维护两个候选者。

投票算法的本质

这是一种消去法。只要某个元素的出现次数严格超过一半,它就是不可被完全抵消的。

相关题目

加载评论中...