当前位置: 首页 > news >正文

LeetCode 面试经典 150 题:多数元素(摩尔投票法详解 + 多解法对比)

在数组类算法题中,“多数元素” 是一道考察 “高效统计” 思路的经典题目。所谓 “多数元素”,是指在数组中出现次数大于 ⌊n/2⌋(n 为数组长度)的元素,且题目明确说明 “存在唯一的多数元素”。这道题的最优解 ——摩尔投票法,能以 O (n) 时间复杂度和 O (1) 空间复杂度解决问题,是面试中高频考察的 “最优解思路”。本文将从题目解读、摩尔投票法原理、步骤演示,到代码实现,再到其他解法对比,帮你彻底掌握这道题的核心逻辑。

一、题目链接与题干解读

首先,你可以通过以下链接直接访问题目,先自行思考解题方向:

LeetCode 题目链接:169.多数元素

题干核心信息

题目要求如下:

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

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例理解

通过两个典型示例,能更直观理解 “多数元素” 的定义:

  • 示例 1:输入 nums = [3,2,3],n=3,⌊3/2⌋=1,“3” 出现 2 次(大于 1),因此输出 3;
  • 示例 2:输入 nums = [2,2,1,1,1,2,2],n=7,⌊7/2⌋=3,“2” 出现 4 次(大于 3),因此输出 2。

二、核心解法:摩尔投票法(Boyer-Moore Voting Algorithm)

摩尔投票法的核心思想是 “多数元素的票数终将盖过其他所有元素的票数之和”。由于多数元素出现次数大于 ⌊n/2⌋,即使其他所有元素联合起来 “对抗” 多数元素,多数元素的 “票数” 也会剩余。这种思路无需统计每个元素的出现次数,仅通过一次遍历即可确定候选多数元素,空间复杂度极低。

1. 摩尔投票法的基本原理

我们可以把整个过程想象成一场 “投票选举”:

  • 每个元素相当于一张 “选票”,投票给对应的 “候选人”(即元素本身);
  • 我们维护一个 “当前候选人”m和 “当前得票数”cnt:
    • 当得票数cnt=0时,说明之前的投票已 “抵消完毕”,需要重新选择新的候选人(即当前遍历到的元素);
    • 当遇到与当前候选人m相同的元素时,得票数cnt加 1(支持当前候选人);
    • 当遇到与当前候选人m不同的元素时,得票数cnt减 1(反对当前候选人,票数抵消);
  • 由于多数元素的出现次数超过数组长度的一半,最终剩余的 “候选人” 必然是多数元素(题目已保证存在多数元素,无需二次验证)。

2. 核心步骤拆解

摩尔投票法仅需一次遍历,具体步骤如下:

  1. 初始化变量:设置 “当前候选人”m(初始值可随意,后续会更新),“当前得票数”cnt = 0;
  1. 遍历数组:对于数组中的每个元素x,执行以下判断:
    • 若cnt == 0:说明之前的票数已抵消,将当前元素x设为新候选人(m = x),并将得票数初始化为 1(cnt = 1);
    • 若cnt != 0:
      • 若x == m:当前元素支持候选人,得票数加 1(cnt += 1);
      • 若x != m:当前元素反对候选人,得票数减 1(cnt -= 1);
  1. 返回结果:遍历结束后,m即为多数元素。

3. 示例演示(以示例 2:nums = [2,2,1,1,1,2,2]为例)

我们一步步跟踪m和cnt的变化,直观感受投票过程:

遍历顺序

当前元素x

cnt初始值

判断逻辑

m更新后

cnt更新后

备注

1

2

0

cnt=0 → 设 m=2,cnt=1

2

1

首次选候选人 2

2

2

1

x==m → cnt+1

2

2

支持候选人 2,票数增加

3

1

2

x!=m → cnt-1

2

1

反对候选人 2,票数减少

4

1

1

x!=m → cnt-1

2

0

反对候选人 2,票数抵消为 0

5

1

0

cnt=0 → 设 m=1,cnt=1

1

1

重新选候选人 1

6

2

1

x!=m → cnt-1

1

0

反对候选人 1,票数抵消为 0

7

2

0

cnt=0 → 设 m=2,cnt=1

2

1

重新选候选人 2

遍历结束后,m=2,与示例 2 的正确答案一致。从过程可见,即使中间出现其他候选人(如 1),但由于多数元素 2 的出现次数更多,最终仍会成为剩余的候选人。

三、其他常见解法(对比参考)

除了摩尔投票法,“多数元素” 还有其他解法,虽然复杂度不如摩尔投票法最优,但能帮助我们从不同角度理解问题,以下简要介绍两种:

1. 哈希表统计法(空间复杂度 O (n))

思路

用哈希表(如 Python 中的字典,Java中的hashMap)记录每个元素的出现次数,遍历数组时更新次数,最后遍历哈希表,返回出现次数大于⌊n/2⌋的元素。

优缺点
  • 优点:逻辑简单直观,易于实现;
  • 缺点:需要额外开辟哈希表空间,空间复杂度为 O (n),不如摩尔投票法高效。

2. 排序法(时间复杂度 O (n log n))

思路

由于多数元素出现次数大于⌊n/2⌋,对数组排序后,数组的 “中间位置”(索引为 n//2)的元素必然是多数元素(例如:n=7 时,索引 3;n=3 时,索引 1,均为多数元素)。

优缺点
  • 优点:代码极简,依赖排序 API 即可实现;
  • 缺点:排序的时间复杂度为 O (n log n),不如摩尔投票法的 O (n) 高效。
代码片段(Python)
class Solution:def majorityElement(self, nums: List[int]) -> int:nums.sort()return nums[len(nums) // 2]

四、复杂度分析(摩尔投票法)

1. 时间复杂度:O (n)

  • 摩尔投票法仅对数组进行一次遍历,每个元素只参与一次 “判断 - 更新” 操作,无嵌套循环;
  • 遍历次数与数组长度 n 成正比,因此时间复杂度为线性的 O (n)。

2. 空间复杂度:O (1)

  • 整个过程只用到了两个额外变量(m和cnt),没有开辟新的数组、哈希表或其他数据结构;
  • 额外空间的使用与数组长度 n 无关,因此空间复杂度为常数级的 O (1)。

五、摩尔投票法代码实现

以下以 Python ,Java为例,实现摩尔投票法:

1,Python

class Solution:def majorityElement(self, nums: List[int]) -> int:cnt = 0m = 0for x in nums:if cnt == 0:m = xcnt = 1else:cnt += 1 if m == x else -1return m

2,Java

class Solution {public int majorityElement(int[] nums) {int cnt = 0, m = 0;for (int x : nums) {if (cnt == 0) {m = x;cnt = 1;} else {cnt += m == x ? 1 : -1;}}return m;}
}

你可以将上述代码复制到 LeetCode 编辑器中测试,完全符合题目要求。

六、总结与拓展

摩尔投票法是解决 “多数元素” 问题的最优解,其核心优势在于 “线性时间 + 常数空间”,这种高效性使其在面试中备受青睐。需要注意的是,摩尔投票法的适用场景有两个前提:

  1. 数组中存在多数元素(若不确定是否存在,需在第一次遍历后进行二次遍历,统计候选元素的出现次数,确认是否满足 “多数” 条件);
  1. 多数元素的定义是 “出现次数大于⌊n/2⌋”(若定义为 “出现次数大于⌊n/3⌋”,则需扩展为 “双候选人摩尔投票法”,可找出最多两个候选元素,再二次验证)。

扩展场景:找出出现次数大于⌊n/3⌋的元素

若题目改为 “找出数组中所有出现次数大于⌊n/3⌋的元素”(LeetCode 229 题),可扩展摩尔投票法为 “双候选人” 模式:

  • 维护两个候选人m1、m2和两个得票数cnt1、cnt2;
  • 遍历数组时,优先给与m1、m2相同的元素加票,否则若cnt1=0或cnt2=0,更新对应的候选人和票数,最后若都不满足,则给两个候选人同时减票;
  • 遍历结束后,需二次验证两个候选人的出现次数是否大于⌊n/3⌋(因可能不存在或存在 1-2 个符合条件的元素)。

掌握摩尔投票法的核心逻辑,不仅能解决 “多数元素” 问题,更能应对其扩展场景,体现算法思维的灵活性。

希望通过本文的讲解,你能不仅学会 “多数元素” 的解法,更能深入理解摩尔投票法的原理,将其灵活应用到类似的 “高频统计” 问题中,提升面试竞争力。


文章转载自:

http://xl1HNNTF.xqxrm.cn
http://AjOuzvca.xqxrm.cn
http://AfdZPFBf.xqxrm.cn
http://BnlE2wKB.xqxrm.cn
http://6KKPeUG5.xqxrm.cn
http://ztg2EVmN.xqxrm.cn
http://LpebYkZq.xqxrm.cn
http://msr1Qq3k.xqxrm.cn
http://xt4chYgO.xqxrm.cn
http://hJLswE1e.xqxrm.cn
http://McWLKcjO.xqxrm.cn
http://npf4HvRt.xqxrm.cn
http://NIMWGGFF.xqxrm.cn
http://HzyA1rGa.xqxrm.cn
http://4mNKTY7h.xqxrm.cn
http://OagoPhEf.xqxrm.cn
http://NYY3NA6s.xqxrm.cn
http://Z1gVYF2W.xqxrm.cn
http://Gp60jTOO.xqxrm.cn
http://k9D5x8QH.xqxrm.cn
http://gKUq9VmG.xqxrm.cn
http://iD7NbycI.xqxrm.cn
http://Rab6ScFO.xqxrm.cn
http://wb7ybS5s.xqxrm.cn
http://4wS1bSmF.xqxrm.cn
http://Aqufuin9.xqxrm.cn
http://yXkVY7Le.xqxrm.cn
http://LjKVTnJW.xqxrm.cn
http://gipIWpUC.xqxrm.cn
http://hd4lv17L.xqxrm.cn
http://www.dtcms.com/a/374721.html

相关文章:

  • CStringArray 和 CStringList
  • 银行业安全用电系统建设与智能化管理探析
  • 20250909_排查10.1.1.190档案库房综合管理系统20250908备份缺失问题+优化scp脚本(把失败原因记录进日志)并测试脚本执行情况
  • 硬件开发_基于STM32单片机的海鲜冷藏车检测系统
  • AI一周事件(2025年9月3日-9月8日)
  • Unity3D发布的文件打包成Windows安装程序
  • 已知两个平面点的坐标、切线方向、曲率,使用牛顿迭代法构造三阶 Bézier 曲线的方法
  • 全球工业互联网大会 | 蓝卓supOS以数据底座,筑牢工业AI基石
  • k8s交互桥梁:走进Client-Go
  • K8S-Node
  • 嵌入式 - ARM(4) 硬件介绍与开发环境搭建
  • 网络上那些在线 PDF 转换工具安全吗?转换 PDF 需要注意什么
  • OneMark 插件试用
  • 专题:2025人形机器人、工业机器人、智能焊接机器人、扫地机器人产业洞察报告 | 附158+份报告PDF、数据仪表盘汇总下载
  • 微服务核心组件实战:Nacos 与 Ribbon 的应用
  • PDF处理控件Aspose.PDF教程:使用 Python 将 PDF 转换为 Base64
  • arm启动代码总结
  • TypeScript学习【一】
  • Day 19: 算法基础与面试理论精通 - 从思想理解到策略掌握的完整体系
  • 基于CNN的航空发动机剩余寿命预测 (MATLAB实现)
  • 已知 inode 号,如何操作文件?Ext 文件系统增删查改底层逻辑拆解
  • 论文阅读,Plug-and-Play Latent Diffusion,Brain Imaging
  • C#(/unity)中的闭包
  • 概率论第六讲—数理统计
  • Oracle RAC共享存储核心技术
  • C++, ffmpeg, libavcodec-RTSP拉流,opencv实时预览
  • 全网首发!Realsense 全新 D555 相机开箱记录与 D435i、L515、D456 横向测评!
  • 基于 Django 与 Bootstrap 构建的现代化设备管理平台
  • 图像金字塔---图像上采样下采样
  • 【ARM】ULINK Pro如何和SWD接口进行连接调试