🗳️ LeetCode 169:多数元素 —— 三种解法 & Boyer–Moore 投票算法的本质理解

2 阅读3分钟

🗳️ LeetCode 169:多数元素 —— 三种解法 & Boyer–Moore 投票算法的本质理解

一道看似简单的数组题,背后却藏着非常优雅的算法思想。

LeetCode 169:多数元素(Majority Element)

题目要求很简单:

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

⚠️ 题目保证:多数元素一定存在


一、先看问题的“数学结构”

如果某个元素出现次数 > n / 2,那意味着:

它的数量 > 所有其他元素数量之和

这条性质,正是后面所有解法的根基。


二、解法一:排序法(最直觉、最稳妥)

💡 思路

  • 将数组排序
  • 出现次数超过一半的元素,一定会占据 中位数位置
class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length / 2];
    }
}

⏱️ 复杂度分析

  • 时间复杂度:O(n log n)
  • 空间复杂度:O(1) (忽略排序栈空间)

✅ 优点

  • 思路极其简单
  • 不容易写错
  • 面试中“兜底解法”

❌ 缺点

  • 没有利用「多数元素 > n/2」这个强约束
  • 排序属于杀鸡用牛刀

👉 能用,但不优雅


三、解法二:HashMap 计数法(通用但偏重)

💡 思路

  • 使用 HashMap 统计每个数字出现的次数
  • 找到 count > n / 2 的元素
class Solution {
    public int majorityElement(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>();
        int majority = 0, maxCount = 0;

        for (int num : nums) {
            int count = map.getOrDefault(num, 0) + 1;
            map.put(num, count);
            if (count > maxCount) {
                maxCount = count;
                majority = num;
            }
        }
        return majority;
    }
}

⏱️ 复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

✅ 优点

  • 通用性极强
  • 好理解、好调试
  • 可扩展到任意频率统计问题

❌ 缺点

  • 使用了额外空间
  • 统计了大量其实不必要的信息

👉 正确但“信息冗余”


四、解法三:Boyer–Moore 投票算法(本题最优解 ⭐)

💡 核心思想(一句话版)

多数元素的数量,大于所有其他元素之和。
把不同的元素两两抵消,最后剩下的一定是多数元素。


🧠 算法直觉解释

我们维护两个变量:

  • candidate:当前候选人
  • count:候选人的“净票数”

规则只有三条:

  1. count == 0 → 更换候选人
  2. 当前元素 == 候选人 → count + 1
  3. 当前元素 ≠ 候选人 → count - 1
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;
    }
}

🔍 为什么它一定正确?

把数组元素分成两类:

  • M:多数元素
  • ¬M:所有非多数元素

每一次 count--,都等价于:

删除一个 M 和一个 ¬M

但由于:

M 的数量 > ¬M 的数量

所以:

  • 非多数元素会被先抵消完
  • 多数元素一定至少剩 1 个
  • 最终留下的 candidate 就是答案

👉 算法的本质是:数量差的不变量


⏱️ 复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

✅ 优点

  • 时间、空间双最优
  • 完全利用题目数学结构
  • 非常优雅

❌ 注意点

  • 必须保证多数元素存在
  • 如果题目条件不满足,需要二次验证

五、三种方法对比总结

方法时间复杂度空间复杂度特点
排序O(n log n)O(1)简单直观
HashMapO(n)O(n)通用但偏重
Boyer–MooreO(n)O(1)最优、最优雅

六、写在最后:为什么 Boyer–Moore 值得反复品味

Boyer–Moore 并不是在“统计最多的元素”,而是在:

利用问题本身的数学约束,把无关信息一层层消掉。

它教会我们的不是一段代码,而是一种算法思想:

当某个元素的“优势不可逆”时,可以用抵消来找答案。