跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Python算法

摩尔投票法详解

摩尔投票法,这是一种用于在无序数组中查找出现次数超过一半元素的线性时间、常数空间算法。文章阐述了算法的核心抵消思想、执行步骤及验证必要性,提供了基础版和简化版的 Python 代码实现。此外,还讲解了如何扩展该算法以查找出现次数超过 n/k 的元素,并对比了其与哈希表法的优劣及适用场景,适合算法学习与面试准备。

Ne0发布于 2026/3/29更新于 2026/10/2699 浏览
摩尔投票法详解

摩尔投票法详解

一、摩尔投票法是什么

摩尔投票法是一种高效的线性时间、常数空间算法,专门用于求解 无序数组中出现次数超过 n/2 的元素(也叫绝对众数),同时可以扩展到求解出现次数超过 n/k 的元素。 核心优势:

  • 时间复杂度:O(n),仅需一次遍历数组;
  • 空间复杂度:O(1),无需额外哈希表存储元素频次,极致优化。 补充说明:该算法的核心逻辑基于一个数学事实——在任何数组中,绝对众数的出现次数比其他所有元素的总和还多,这是算法能成立的根本前提。

二、算法核心思想

摩尔投票法的核心可以拆解为 「抵消」和「计数」 两个关键步骤,逻辑极其简洁:

  1. 抵消阶段:遍历数组时,维护一个候选众数candidate 和一个计数变量count。
    • 如果 count == 0,说明当前没有候选众数,将当前元素设为 candidate;
    • 如果当前元素 == candidate,则 count += 1(候选众数的支持数 +1);
    • 如果当前元素 != candidate,则 count -= 1(不同元素两两抵消,支持数 -1)。
  2. 验证阶段(可选但建议必做):遍历结束后,得到的 candidate 只是可能的众数,需要再次遍历数组,统计其真实出现次数,确认是否超过 n/2。
    • 原因:如果数组中不存在绝对众数,算法也会返回一个候选值,必须验证才能保证结果正确。
核心原理拆解

算法的本质是利用「多数元素的压倒性优势」:即使每次抵消一对不同元素,最终剩下的元素一定是绝对众数(如果存在的话)。 举个例子:数组 [2,2,1,1,1,2,2],绝对众数是 2(出现 5 次,超过 7/2=3.5)。遍历过程中,2 和 1 不断抵消,但 2 的数量更多,最终会成为候选值。

三、算法执行步骤 固定流程

以求解「数组中出现次数超过 n/2 的元素」为例,步骤标准化,无任何变种:

  1. 初始化:candidate = None,count = 0;
  2. 第一次遍历(抵消阶段):
    • 遍历数组中的每个元素 num;
    • 若 count == 0,令 candidate = num;
    • 若 num == candidate,count += 1,否则 count -= 1;
  3. 第二次遍历(验证阶段):
    • 统计 candidate 在数组中的出现次数 total;
    • 若 total > n/2,返回 candidate,否则返回 None(无绝对众数)。

四、完整实现代码 两种版本

版本 1 基础版(含验证阶段,推荐)
def majority_element():
    
    candidate = 
    count = 
     num  nums:
         count == :
            candidate = num
        count +=   num == candidate  -
    
    
    total = 
     num  nums:
         num == candidate:
            total += 
     candidate  total > (nums) //   
nums
# 步骤 1:抵消阶段,找候选众数
None
0
for
in
if
0
1
if
else
1
# 步骤 2:验证阶段,确认是否为绝对众数
0
for
in
if
1
return
if
len
2
else
None
版本 2 简化版(假设数组一定存在绝对众数,笔试常用)

如果题目明确说明「数组中一定存在绝对众数」,可以省略验证阶段,直接返回候选值:

def majority_element_simple(nums):
    candidate = None
    count = 0
    for num in nums:
        if count == 0:
            candidate = num
        count += 1 if num == candidate else -1
    return candidate
测试案例 可直接运行
# 测试 1:存在绝对众数
nums1 = [2,2,1,1,1,2,2]
print(majority_element(nums1))  # 输出 2

# 测试 2:不存在绝对众数
nums2 = [1,2,3,4,5]
print(majority_element(nums2))  # 输出 None

# 测试 3:简化版(假设存在)
print(majority_element_simple(nums1))  # 输出 2

五、算法扩展 求解出现次数超过 n/k 的元素

摩尔投票法可以扩展到求解 数组中出现次数超过 n/k 的所有元素,这是面试高频进阶考点,核心逻辑是「最多有 k-1 个元素满足条件」。

核心思路
  1. 维护一个候选字典candidates,存储最多 k-1 个候选元素及其计数;
  2. 遍历数组时:
    • 若当前元素在候选字典中,计数 +1;
    • 若候选字典的大小 < k-1,将当前元素加入字典,计数设为 1;
    • 否则,将所有候选元素的计数 -1,若计数变为 0,从字典中删除该元素;
  3. 遍历结束后,验证候选字典中的元素,统计其真实出现次数,确认是否超过 n/k。
扩展版代码(求解超过 n/3 的元素)
def majority_element_k(nums, k=3):
    # 步骤 1:抵消阶段,最多 k-1 个候选
    candidates = {}
    for num in nums:
        if num in candidates:
            candidates[num] += 1
        elif len(candidates) < k - 1:
            candidates[num] = 1
        else:
            # 所有候选计数 -1,删除计数为 0 的
            to_remove = []
            for key in candidates:
                candidates[key] -= 1
                if candidates[key] == 0:
                    to_remove.append(key)
            for key in to_remove:
                del candidates[key]
    
    # 步骤 2:验证阶段
    n = len(nums)
    res = []
    for key in candidates:
        total = nums.count(key)
        if total > n // k:
            res.append(key)
    return res

# 测试:找超过 n/3 的元素
nums = [3,2,3,1,1,2,2]
print(majority_element_k(nums))  # 输出 [2,3]

六、算法复杂度分析

时间复杂度 O(n)
  • 基础版:两次遍历数组,总时间复杂度为 O(n)+O(n)=O(n);
  • 扩展版:遍历数组的时间为 O(n),验证阶段的时间为 O(k×n),由于 k 是常数,整体仍为 O(n)。
空间复杂度 O(1)(基础版)/ O(k)(扩展版)
  • 基础版:仅使用两个变量 candidate 和 count,空间复杂度为常数级;
  • 扩展版:候选字典最多存储 k-1 个元素,空间复杂度为 O(k),k 为常数时也视为常数空间。

七、关键细节 避坑指南 笔试必看

细节 1 验证阶段不能省略

这是最容易踩的坑!如果数组中不存在绝对众数,算法也会返回一个候选值,必须通过验证才能确认结果。 例如数组 [1,2,3],算法会返回 3,但 3 的出现次数只有 1 次,不满足超过 3/2=1.5 的条件。

细节 2 算法的适用前提

摩尔投票法仅适用于求解出现次数超过 n/k 的元素,如果题目要求「出现次数最多的元素」(不要求超过 n/k),则需要用哈希表统计频次,无法使用该算法。

细节 3 扩展版的候选数量限制

求解超过 n/k 的元素时,最多有 k-1 个元素满足条件,这是数学定理。 例如 k=2 时最多 1 个,k=3 时最多 2 个,利用这个性质可以限制候选字典的大小。

细节 4 抵消阶段的顺序不影响结果

无论数组的遍历顺序如何,最终得到的候选值都是相同的(如果存在绝对众数),因为抵消的本质是「不同元素两两消除」,与顺序无关。

八、摩尔投票法 vs 哈希表法 核心对比

两者都是求解众数问题的常用方法,面试中经常作为对比考点,必须牢记区别和适用场景:

特性摩尔投票法哈希表法
时间复杂度O(n)O(n)
空间复杂度O(1)(基础版)O(n)
适用场景找超过 n/k 的元素找任意频次的元素
优点空间效率极高,无需额外存储逻辑简单,适用范围广
缺点适用场景有限,需要验证空间开销大,数据量大时内存紧张
适用场景选择
  1. 题目明确要求「找超过 n/2 的元素」且空间复杂度要求 O(1) → 优先用 摩尔投票法;
  2. 题目要求「找出现次数最多的元素」或「统计所有元素的频次」→ 用 哈希表法;
  3. 工程开发中,如果数据量不大,哈希表法更易实现和维护;如果内存受限,摩尔投票法是最优选择。

九、核心总结

  1. 摩尔投票法 是求解绝对众数的最优算法,时间 O(n)、空间 O(1),核心是「抵消思想」;
  2. 基础版两步走:抵消阶段找候选 + 验证阶段确认,验证阶段不能省略;
  3. 扩展版可求解超过 n/k 的元素,最多有 k-1 个候选,空间复杂度 O(k);
  4. 算法的适用前提是「元素出现次数超过 n/k」,不满足则无法使用;
  5. 对比哈希表法,摩尔投票法的优势是空间效率,劣势是适用场景有限;
  6. 核心原理:多数元素的出现次数超过其他元素总和,抵消后必然剩余。

目录

  1. 摩尔投票法详解
  2. 一、摩尔投票法是什么
  3. 二、算法核心思想
  4. 核心原理拆解
  5. 三、算法执行步骤 固定流程
  6. 四、完整实现代码 两种版本
  7. 版本 1 基础版(含验证阶段,推荐)
  8. 版本 2 简化版(假设数组一定存在绝对众数,笔试常用)
  9. 测试案例 可直接运行
  10. 测试 1:存在绝对众数
  11. 测试 2:不存在绝对众数
  12. 测试 3:简化版(假设存在)
  13. 五、算法扩展 求解出现次数超过 n/k 的元素
  14. 核心思路
  15. 扩展版代码(求解超过 n/3 的元素)
  16. 测试:找超过 n/3 的元素
  17. 六、算法复杂度分析
  18. 时间复杂度 O(n)
  19. 空间复杂度 O(1)(基础版)/ O(k)(扩展版)
  20. 七、关键细节 避坑指南 笔试必看
  21. 细节 1 验证阶段不能省略
  22. 细节 2 算法的适用前提
  23. 细节 3 扩展版的候选数量限制
  24. 细节 4 抵消阶段的顺序不影响结果
  25. 八、摩尔投票法 vs 哈希表法 核心对比
  26. 适用场景选择
  27. 九、核心总结

更多推荐文章

查看全部
  • Dify + Skill 本地部署大模型智能体:构建企业级 AI Agent 系统
  • Milvus 实战:Attu 可视化安装与 Python 整合指南
  • WebGPU 全面解析:新一代 Web 图形与计算 API
  • Mem0:为 LLM 提供个性化记忆的开源项目实战指南
  • MyBatis 缓存机制详解:一级与二级缓存
  • 学生与教育工作者免费获取 GitHub Copilot 权限指南
  • 程序员靠谱副业盘点:内容、技术与多元变现路径
  • 使用 Nanoedge AI Studio 和 STM32Cube AI 实现 STM32 边缘计算
  • 网络安全、信息安全与数据安全的概念辨析
  • 手把手教你从零微调大模型
  • Python 语言概述及职场核心应用场景分析
  • RAG 实战指南:核心原理、工作流程与常见挑战解析
  • 网络安全学习指南:核心知识与路径
  • Flutter 使用 tiktoken 库进行鸿蒙端侧 Token 计算与适配指南
  • 程序员兼职接单平台推荐与避坑指南
  • Diff-eRank:基于有效秩的大模型去噪能力评估新指标
  • VibeThinker-1.5B-APP:小模型在编程与数学推理中的优势与部署
  • FPGA 常见故障排查:10 个高频坑位
  • Apache POI 导出 Excel 遇 NoClassDefFoundError:深入分析与解决方案
  • 重构建议 Prompt 提升代码可读性实战指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online