【简单】169. 多数元素

0 阅读2分钟

给定一个大小为 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) 的算法解决此问题。


1. 核心矛盾:谁才是真正的王者?

题目要求找出现次数 大于 n/2n/2 的元素。

  • 暴力法: 统计每个数出现的次数(需要哈希表,O(n)O(n) 空间)。
  • 进阶挑战: 能不能不用额外空间(O(1)O(1) 空间),且只看一遍数组(O(n)O(n) 时间)就找到它?

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. 为什么这个算法行得通?

  • 前提条件: 题目明确说明了“给定的数组总是存在多数元素”。如果没有这个前提,最后活下来的数还需要再遍历一遍数组验证一下频率。

  • 空间换时间?不: 哈希表法像是在登记所有人的户口。

    • 摩尔投票法像是在战场上搞“对子”。
  • 复杂度:

    • 时间复杂度: O(n)O(n) —— 只需要走一遍迷宫。
    • 空间复杂度: O(1)O(1) —— 只需要两个变量(擂主名和战斗力值),不需要开辟新数组。