给定一个大小为 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 * 104-109 <= nums[i] <= 109- 输入保证数组中一定有一个多数元素。
进阶: 尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
1. 核心矛盾:谁才是真正的王者?
题目要求找出现次数 大于 的元素。
- 暴力法: 统计每个数出现的次数(需要哈希表, 空间)。
- 进阶挑战: 能不能不用额外空间( 空间),且只看一遍数组( 时间)就找到它?
2. 生活案例:最强王者大乱斗
想象一场生存游戏,数组里的每个数字代表一个阵营的人。
- 规则: 不同阵营的人遇到就会“同归于尽”(1换1)。相同阵营的人遇到就会“抱团生存”。
- 逻辑: 因为“多数元素”阵营的人数超过了总人数的一半,所以哪怕他们和其他所有人 1 换 1,最后活下来的也一定是这个阵营的人!
3. 完整代码实现(带详尽注释)
JavaScript
/**
* @param {number[]} nums
* @return {number}
*/
var majorityElement = function(nums) {
// result 用来记录当前留在场上的“擂主”
let result = null;
// count 用来记录当前“擂主”阵营还剩下多少战斗力
let count = 0;
for (let num of nums) {
// --- 第一步:如果场上没人了,当前数字直接占领阵地成为新擂主 ---
if (count === 0) {
result = num;
}
// --- 第二步:阵营判断 ---
if (result === num) {
// 如果遇到同阵营的人,战斗力 +1(抱团)
count++;
} else {
// 如果遇到不同阵营的人,同归于尽,战斗力 -1(消消乐)
count--;
}
}
// 因为多数元素占了一半以上,最后活下来的 result 一定是它
return result;
};
4. 为什么这个算法行得通?
-
前提条件: 题目明确说明了“给定的数组总是存在多数元素”。如果没有这个前提,最后活下来的数还需要再遍历一遍数组验证一下频率。
-
空间换时间?不: 哈希表法像是在登记所有人的户口。
- 摩尔投票法像是在战场上搞“对子”。
-
复杂度:
- 时间复杂度: —— 只需要走一遍迷宫。
- 空间复杂度: —— 只需要两个变量(擂主名和战斗力值),不需要开辟新数组。