多数元素
问题简介
题解github地址: https://github.com/swf2020/LeetCode-Hot100-Solutions
题目描述
给定一个大小为 n 的数组 nums,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例说明
✅ 示例 1:
输入:nums = [3,2,3]
输出:3
✅ 示例 2:
输入:nums = [2,2,1,1,1,2,2]
输出:2
📌 约束条件:
n == nums.length1 <= n <= 5 * 10⁴-10⁹ <= nums[i] <= 10⁹
解题思路
方法一:哈希表计数(直观解法)
💡 思路:
- 遍历数组,用哈希表记录每个元素出现的次数
- 当某个元素的计数超过
n/2时,直接返回该元素
✅ 优点: 思路简单直观
❌ 缺点: 需要额外的 O(n) 空间
方法二:排序法
💡 思路:
- 对数组进行排序
- 由于多数元素出现次数超过一半,排序后中间位置的元素必定是多数元素
✅ 优点: 实现简单,空间复杂度较低
❌ 缺点: 时间复杂度较高 O(n log n)
方法三:Boyer-Moore 投票算法(最优解)
💡 核心思想:
- 多数元素的数量比其他所有元素的总和还要多
- 维护一个候选元素和计数器
- 遇到相同元素计数器+1,不同元素计数器-1
- 当计数器为0时,更换候选元素
✅ 优点: 时间复杂度 O(n),空间复杂度 O(1)
✅ 这是面试官最希望看到的解法
代码实现
java:Java
// 方法一:哈希表计数
class Solution {
public int majorityElement(int[] nums) {
Map<Integer, Integer> countMap = new HashMap<>();
int n = nums.length;
for (int num : nums) {
countMap.put(num, countMap.getOrDefault(num, 0) + 1);
if (countMap.get(num) > n / 2) {
return num;
}
}
return -1; // 不会执行到这里
}
}
// 方法二:排序法
class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length / 2];
}
}
// 方法三:Boyer-Moore 投票算法
class Solution {
public int majorityElement(int[] nums) {
int candidate = 0;
int count = 0;
for (int num : nums) {
if (count == 0) {
candidate = num;
}
count += (num == candidate) ? 1 : -1;
}
return candidate;
}
}
go:Go
// 方法一:哈希表计数
func majorityElement(nums []int) int {
countMap := make(map[int]int)
n := len(nums)
for _, num := range nums {
countMap[num]++
if countMap[num] > n/2 {
return num
}
}
return -1 // 不会执行到这里
}
// 方法二:排序法
import "sort"
func majorityElement(nums []int) int {
sort.Ints(nums)
return nums[len(nums)/2]
}
// 方法三:Boyer-Moore 投票算法
func majorityElement(nums []int) int {
candidate := 0
count := 0
for _, num := range nums {
if count == 0 {
candidate = num
}
if num == candidate {
count++
} else {
count--
}
}
return candidate
}
示例演示
让我们用 Boyer-Moore 投票算法 演示示例 [2,2,1,1,1,2,2]:
| 步骤 | 当前元素 | 候选元素 | 计数器 | 说明 |
|---|---|---|---|---|
| 1 | 2 | 2 | 1 | 初始化候选为2 |
| 2 | 2 | 2 | 2 | 相同元素,计数+1 |
| 3 | 1 | 2 | 1 | 不同元素,计数-1 |
| 4 | 1 | 2 | 0 | 不同元素,计数-1 |
| 5 | 1 | 1 | 1 | 计数为0,更换候选为1 |
| 6 | 2 | 1 | 0 | 不同元素,计数-1 |
| 7 | 2 | 2 | 1 | 计数为0,更换候选为2 |
最终候选元素为 2,正确!
答案有效性证明
Boyer-Moore 投票算法正确性证明:
关键观察: 多数元素的数量 > n/2,其他所有元素的总和 < n/2
证明过程:
- 配对消除: 将多数元素与其他元素一一配对消除
- 剩余元素: 由于多数元素数量更多,消除后必然有剩余
- 最终候选: 算法保证最后剩下的就是多数元素
数学表达:
- 设多数元素出现次数为
m,其他元素总次数为k - 则
m > n/2,k = n - m < n/2 - 因此
m > k,配对消除后多数元素仍有剩余
复杂度分析
| 方法 | 时间复杂度 | 空间复杂度 | 是否最优 |
|---|---|---|---|
| 哈希表计数 | O(n) | O(n) | ❌ |
| 排序法 | O(n log n) | O(1) 或 O(n)* | ❌ |
| Boyer-Moore | O(n) | O(1) | ✅ |
*注:某些排序算法可能需要 O(n) 额外空间
问题总结
📌 核心要点:
- 多数元素的定义是出现次数 严格大于
n/2 - 题目保证一定存在多数元素,无需验证结果
- Boyer-Moore 投票算法是解决此类问题的经典方法
💡 扩展思考:
- 如果题目要求找出所有出现次数超过
n/k的元素,该如何处理? - Boyer-Moore 算法可以推广到寻找多个候选元素的情况
✅ 面试建议:
- 先给出哈希表的直观解法
- 优化到排序法
- 最终展示 Boyer-Moore 投票算法
- 清晰解释算法原理和正确性证明