解锁 LeetCode 简单题:“多数元素”的多解之道
一、题目描述
给定一个大小为 n
的数组 nums
,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋
的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例
- 示例 1:
- 输入:
nums = [3,2,3]
- 输出:
3
- 输入:
- 示例 2:
- 输入:
nums = [2,2,1,1,1,2,2]
- 输出:
2
- 输入:
二、解题思路与多方法实现
方法一:哈希表统计法
思路
使用哈希表来统计数组中每个元素出现的次数。遍历数组,对于每个元素,若它已经在哈希表中,就将其对应的计数加 1;若不在哈希表中,就将其加入哈希表并将计数初始化为 1。最后遍历哈希表,找出计数大于 ⌊ n/2 ⌋
的元素。
代码实现(Java)
import java.util.HashMap;
import java.util.Map;
public class MajorityElement {
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);
}
// 找出多数元素
for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
if (entry.getValue() > n / 2) {
return entry.getKey();
}
}
return -1; // 根据题目假设,这里不会执行到
}
}
代码解释
- 初始化哈希表:创建一个
HashMap
类型的countMap
用于存储元素及其出现的次数。 - 统计元素出现次数:使用增强
for
循环遍历数组nums
,对于每个元素num
,使用getOrDefault
方法获取其当前的计数,如果元素不在哈希表中,返回默认值 0,然后将计数加 1 并更新到哈希表中。 - 查找多数元素:遍历哈希表的键值对,对于每个键值对,检查其值(即元素的出现次数)是否大于
n / 2
,如果是,则返回该键(即元素本身)。
方法二:排序法
思路
由于多数元素出现的次数大于 ⌊ n/2 ⌋
,那么在对数组进行排序后,数组中间位置的元素必然是多数元素。
代码实现(Java)
import java.util.Arrays;
public class MajorityElementSort {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length / 2];
}
}
代码解释
- 数组排序:使用
Arrays.sort
方法对数组nums
进行排序。Java 中的Arrays.sort
方法通常采用高效的排序算法,如快速排序或归并排序,平均时间复杂度为 O ( n l o g n ) O(n log n) O(nlogn)。 - 返回多数元素:排序后,数组中间位置(索引为
nums.length / 2
)的元素就是多数元素,直接返回该元素。
方法三:摩尔投票法
思路
摩尔投票法的核心思想是利用多数元素出现次数多于其他元素出现次数总和的特性。我们可以维护一个候选多数元素 candidate
和一个计数 count
。遍历数组,当 count
为 0 时,将当前元素设为候选多数元素;若当前元素与候选多数元素相同,则 count
加 1,否则 count
减 1。最后剩下的候选多数元素就是真正的多数元素。
代码实现(Java)
public class MajorityElementBoyerMoore {
public int majorityElement(int[] nums) {
int candidate = nums[0];
int count = 1;
// 投票过程
for (int i = 1; i < nums.length; i++) {
if (nums[i] == candidate) {
count++;
} else {
count--;
if (count == 0) {
candidate = nums[i];
count = 1;
}
}
}
return candidate;
}
}
代码解释
- 初始化候选多数元素和计数:将数组的第一个元素设为候选多数元素
candidate
,并将计数count
初始化为 1。 - 投票过程:从数组的第二个元素开始遍历,若当前元素与候选多数元素相同,
count
加 1;若不同,count
减 1。当count
减为 0 时,将当前元素设为新的候选多数元素,并将count
重新设为 1。 - 返回多数元素:遍历结束后,
candidate
即为多数元素,返回该元素。
三、复杂度分析
时间复杂度
- 哈希表统计法:遍历数组的时间复杂度为 O ( n ) O(n) O(n),遍历哈希表的时间复杂度也为 O ( n ) O(n) O(n),所以总的时间复杂度为 O ( n ) O(n) O(n)。
- 排序法:排序操作的时间复杂度为 O ( n l o g n ) O(n log n) O(nlogn),获取中间元素的时间复杂度为 O ( 1 ) O(1) O(1),因此总的时间复杂度为 O ( n l o g n ) O(n log n) O(nlogn)。
- 摩尔投票法:只需要遍历一次数组,时间复杂度为 O ( n ) O(n) O(n)。
空间复杂度
- 哈希表统计法:哈希表最多需要存储
n
个不同的元素,空间复杂度为 O ( n ) O(n) O(n)。 - 排序法:排序算法的空间复杂度取决于具体实现,通常为 O ( l o g n ) O(log n) O(logn)(如快速排序的递归栈空间),获取中间元素的空间复杂度为 O ( 1 ) O(1) O(1),总体空间复杂度主要由排序决定,为 O ( l o g n ) O(log n) O(logn)。
- 摩尔投票法:只使用了常数级的额外空间(
candidate
和count
),空间复杂度为 O ( 1 ) O(1) O(1)。
四、总结
“多数元素”这道简单题为我们展示了不同的算法思路和数据结构的应用。哈希表统计法直观易懂,利用哈希表的特性进行元素计数;排序法巧妙地利用了多数元素在排序后数组中的位置特点;摩尔投票法则以其高效的时间和空间复杂度脱颖而出,是解决此类问题的最优解。通过对这道题的多种解法的学习,我们可以在面对不同场景时,根据时间和空间的要求选择最合适的算法。希望大家在今后的算法学习中,能够灵活运用这些方法,提升自己的解题能力。