跳到正文
第 169 题简单

多数元素

Majority Element

数组中有一个元素出现次数严格超过一半,找出这个多数元素。

在力扣中练习

题目描述

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

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

 

示例 1:

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

示例 2:

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

 

提示:
  • n == nums.length
  • 1 <= n <= 5 * 104
  • -109 <= nums[i] <= 109
  • 输入保证数组中一定有一个多数元素。

 

进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。

解题思路

主解法

摩尔投票 · 两两抵消

不同的元素互相抵消,超过一半的那个一定留到最后

先想哈希计数:统计每个数出现多少次,时间 O(n)、空间 O(n)。排序也可以:多数元素超过一半,排序后一定落在中点,但时间是 O(n log n)。

摩尔投票利用一个更强的事实:把不同的两个元素删掉,多数元素仍然是多数元素。因为每次抵消最多删掉一个多数元素和一个其他元素,它的数量仍然超过剩余元素的一半。

算法只维护两个量:候选 candidate 和票数 count。

  • count == 0 时,当前数接管候选;
  • 与候选相同,count +1;
  • 与候选不同,count -1。

自拟主例 [5,1,2,2,5,5,5] 中,第一个 5 和 1 抵消后 count 归零;两个 2 让候选变成 2,随后 5 把票数追平;最后一个 5 在 count 归零后重新接管,并以 1 票结束。

分步图解

x = 2 与候选 2 相同,count +1 变为 1。

x
2
candidate
2
count
1
nums
0
1
2
3
4
5
6
5
1
2+1
2
5
5
5
i
候选 2 · 计票 1
1 / 4
1 把票数打到 0 后,2 接管候选;连续两个 2 把票数推到 2,随后 5 又开始追。
分步图解

x = 5 与候选 2 不同,计票归零;下一票会重新选候选。

x
5
candidate
2
count
0
nums
0
1
2
3
4
5
6
5
1
2
2
5
5−1
5
i
候选 2 · 计票 0
1 / 3
最后一个 5 在 count 归零后重新接管候选,并以 1 票结束。

时间 O(n),空间 O(1)。

时间 O(n)空间 O(1)

易错点

  • 先比较再判断 count == 0:空白状态下的旧候选会先被投一票。
  • count 归零后不换候选:后续票数会记到已经失去支持的候选头上。
  • 没有“多数元素一定存在”的前提时直接返回候选:需要第二遍验证它是否真的超过一半。
对照解法

排序 · 取中点

多数元素超过一半,排序后必然覆盖中点

代码最短,时间 O(n log n),还会修改输入数组;主解法摩尔投票是 O(n)、O(1),更适合大数据。

如果某个元素出现次数严格大于 n/2,那么排序后它一定占据中点位置 n/2。

原因很直观:把相同的数排在一起,最长的连续段长度超过一半,不可能完全落在中点的左侧或右侧。所以直接返回 nums[n/2] 即可。

时间 O(n log n),空间取决于排序实现(通常 O(log n) 递归栈)。它不依赖“多数元素一定存在”的额外验证,但如果题目不保证,仍然需要再数一遍。

  1. 1排序数组。
  2. 2返回 nums[n/2]。
时间 O(n log n)空间 O(log n)

易错点

  • 下标取成 (n-1)/2:n 为偶数时中点有两个,取右边那个才是安全的 n/2。
  • 直接修改输入数组:如果调用方还要用原顺序,需要先复制。

代码与演示

主解法配有图解;对照解法用于比较思路、复杂度或实现方式。

演示输入nums = [5,1,2,2,5,5,5]

先假设第一个数是候选,count 从 0 开始;count 为 0 时,下一个数会接管候选。

candidate
5
count
0
nums
0
1
2
3
4
5
6
5
1
2
2
5
5
5
候选 5 · 计票 0
1 / 9
查看 C++ 代码

延伸阅读

  • 摩尔投票可以扩展到“出现次数超过 n/k”的元素;维护 k-1 个候选,最后逐一验证。
  • 如果没有“多数元素一定存在”的保证,必须第二遍扫描确认候选的出现次数。