169. 多数元素

简单 含交互动画

学完这道你应该能: 说清它为什么用「摩尔投票」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

AI 私教 · 就着本题动画讲
卡住了不用硬扛,按 8 步把这题真正走通。
从读题、试解、找法到手写代码,边问边打勾,适合第一次系统学算法的同学。

题目描述

数组里有个数出现次数超过一半(> ⌊n/2⌋),找出它。题目保证它一定存在。

nums = [2, 2, 1, 1, 1, 2, 2]
输出 = 2 (2 出现 4 次 > 7/2)

思路解析

多数派的票比其它所有数加起来还多。让不同的数互相抵消,抵到最后还站着的,必然是多数派。

准备:准备两个变量:当前候选是谁、它手里有多少票数。开局都为空。

第 1 个 · 票数为 0 → 立候选:票数是 0,就让当前的 2 上位当候选,并记 1 票。

第 2 个 · 同票 +1:又遇到 2,和候选一样,给它加一票,票数变 2。

第 3 个 · 异票 -1:来了个 1,和候选 2 不同,抵消一票,票数降到 1。注意:候选不换,只是减票。

第 4 个 · 异票 -1 → 归零:又一个 1,票数减到 0——前面这段 2 和 1 正好两两抵消干净,谁也没赢。

第 5 个 · 票数为 0 → 换候选:关键一步:票数归零了,就换候选——现在候选暂时变成 1。别慌,后面会被顶回来。

第 6 个 · 异票 -1 → 归零:又来个 2,把候选 1 的票也抵消到 0。

第 7 个 · 票数为 0 → 换回 2:最后一个 2,票数又是 0,候选换回 2。扫描结束。

结束 · 候选就是答案:中途候选一度变成 1,但 2 太多了,总能把它顶回来。最后的候选 2 就是答案。

正确性来自题目条件:多数元素超过一半,比其它所有数加起来还多。一对一抵消,它永远消不完。

想清楚「归零换人」这一步,你就真懂摩尔投票了。

参考代码(Python)

Python
1def majorityElement(nums):
2    candidate, count = None, 0      # 候选人、它的票数
3    for x in nums:
4        if count == 0:             # 票数归零,才换候选
5            candidate = x
6        count += 1 if x == candidate else -1   # 同 +1,异 -1
7    return candidate               # 题目保证多数元素一定存在

复杂度分析

  • 时间复杂度:O(n) —— 只扫一遍数组
  • 空间复杂度:O(1) —— 只用候选、票数两个变量,不开哈希表

套路模板

记住这个「票数归零换人、同加异减」的模板,求过半元素一招通用。

Python
1candidate, count = None, 0
2for x in arr:
3    if count == 0:
4        candidate = x
5    count += 1 if x == candidate else -1
6# candidate = 出现次数过半的那个

易错点

  • 错误写法:一遇到不同的数就马上换候选正确写法:只有 票数 count == 0 时才换候选(抵消是「减一票」,不是立刻改人;减到 0 才轮下一个上)
  • 错误写法:同票 -1、异票 +1(加减写反)正确写法:同票 +1、异票 -1(要让多数派把票攒住、把少数派抵消干净,方向不能反)
  • 错误写法:不保证多数元素存在时也直接返回候选正确写法:通用情形要再扫一遍、数候选真实票数是否过半(摩尔投票只「选出」候选不验证;本题保证存在才可省掉第二遍)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →206. 反转链表 ← 返回题库