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

力扣169:多数元素-抵消法和哈希表

题目描述

给定一个大小为 n 的数组 nums,返回其中的多数元素。多数元素是指在数组中出现次数大于 ⌊n/2⌋ 的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。

方法一:摩尔投票法(最优解)

核心思想

这个算法基于一个关键的抵消思想:由于多数元素的数量超过总数的一半,那么多数元素与其他元素一一抵消后,最后剩下的必然是多数元素。

算法步骤

  1. 初始化候选元素candidate和计数器count为 0

  2. 遍历数组中的每个元素:

    • 如果count为 0,将当前元素设为候选元素

    • 如果当前元素等于候选元素,count加 1

    • 如果当前元素不等于候选元素,count减 1

  3. 遍历结束后,候选元素就是多数元素

代码实现

class Solution { public int majorityElement(int[] nums) { int ans = 0; // 存储当前候选的多数元素 int hp = 0; // 计数器,表示当前候选元素的"生命值" for (int x : nums) { if (hp == 0) { // 生命值为0,需要新的候选 ans = x; // 设置x为新的候选元素 hp = 1; // 初始生命值为1 } else { // 已经有候选元素 if (ans == x) { // 如果当前元素和候选相同 hp += 1; // 生命值+1(支持者增加) } else { // 如果当前元素和候选不同 hp -= 1; // 生命值-1(相互抵消) } } } return ans; // 最终剩下的候选就是多数元素 } }
//简化 class Solution { public int majorityElement(int[] nums) { int ans = 0; int hp = 0; for (int num : nums) { if (hp == 0) { ans = num; } count += (num == ans) ? 1 : -1; } return ans; } }

复杂度分析

  • 时间复杂度:O(n),只需遍历一次数组

  • 空间复杂度:O(1),只使用了常数级别的额外空间

算法优势

这是解决多数元素问题的最优算法,特别适合空间要求严格的场景。它巧妙地将问题转化为"擂台比武",每个元素要么支持当前候选(同门师兄弟),要么反对(敌人),最终多数元素会胜出。

方法二:哈希表法(最直观)

核心思想

使用哈希表记录每个元素出现的次数,然后找到出现次数超过一半的元素。这是最直观的解法,也是面试中最容易想到的方法。

算法步骤

  1. 创建一个哈希表来记录每个数字出现的次数

  2. 遍历数组,更新哈希表中每个数字的计数

  3. 遍历哈希表,找到出现次数超过 n/2 的元素

代码实现

class Solution { public int majorityElement(int[] nums) { // 创建哈希表 Map<Integer, Integer> countMap = new HashMap<>(); // 统计每个数字出现的次数 for (int num : nums) { countMap.put(num, countMap.getOrDefault(num, 0) + 1); // 可以在统计过程中直接判断,提高效率 if (countMap.get(num) > nums.length / 2) { return num; } } // 理论上不会执行到这里 return -1; } }

简化版本

class Solution { public int majorityElement(int[] nums) { Map<Integer, Integer> map = new HashMap<>(); int maxCount = 0; int result = 0; for (int num : nums) { int count = map.getOrDefault(num, 0) + 1; map.put(num, count); // 在遍历过程中更新最大值 if (count > maxCount) { maxCount = count; result = num; } } return result; } }

复杂度分析

  • 时间复杂度:O(n),需要遍历一次数组,哈希表操作平均O(1)

  • 空间复杂度:O(k),其中k是数组中不同元素的数量,最坏情况下为O(n)

方法对比与选择建议

特性Boyer-Moore算法哈希表统计法
时间复杂度O(n)O(n)
空间复杂度O(1)O(n)
实现难度中等(需要理解算法原理)简单(直观易懂)
是否需要验证需要(如果不确定存在多数元素)不需要(遍历时即可判断)
实际性能更快(常数时间小)稍慢(哈希表操作有开销)
适用场景空间限制严格时代码可读性优先时

选择建议

  1. 面试场景:先讲解Boyer-Moore算法,展示算法优化思想

  2. 实际工作:如果对空间要求不高,使用哈希表法更稳妥

  3. 比赛/刷题:追求极致性能用Boyer-Moore,追求快速实现用哈希表

常见变种问题

1. 寻找出现次数超过 n/3 的元素

class Solution { public List<Integer> majorityElement(int[] nums) { // 最多有两个这样的元素 List<Integer> result = new ArrayList<>(); // 使用Boyer-Moore算法的扩展版本 int candidate1 = 0, candidate2 = 0; int count1 = 0, count2 = 0; for (int num : nums) { if (num == candidate1) { count1++; } else if (num == candidate2) { count2++; } else if (count1 == 0) { candidate1 = num; count1 = 1; } else if (count2 == 0) { candidate2 = num; count2 = 1; } else { count1--; count2--; } } // 验证候选元素是否真的超过n/3 count1 = 0; count2 = 0; for (int num : nums) { if (num == candidate1) count1++; else if (num == candidate2) count2++; } if (count1 > nums.length / 3) result.add(candidate1); if (count2 > nums.length / 3) result.add(candidate2); return result; } }

2. 寻找出现次数最多的元素(众数)

class Solution { public int majorityElement(int[] nums) { // 使用哈希表统计频率 Map<Integer, Integer> countMap = new HashMap<>(); int maxCount = 0; int result = nums[0]; for (int num : nums) { int count = countMap.getOrDefault(num, 0) + 1; countMap.put(num, count); if (count > maxCount) { maxCount = count; result = num; } } return result; } }

总结

多数元素问题是一个经典的算法问题,展示了不同解法之间的权衡:

  1. Boyer-Moore投票算法是空间最优解,体现了算法设计的巧妙性

  2. 哈希表统计法是最直观的解,易于理解和实现

  3. 排序法虽然简单,但时间复杂度较高

  4. 分治法展示了递归解决问题的思路

在实际应用中,根据具体需求选择合适的解法。如果空间限制严格,Boyer-Moore算法是最佳选择;如果追求代码可读性和维护性,哈希表法更合适。无论选择哪种方法,理解其背后的原理和适用场景才是最重要的。

刷题建议:掌握Boyer-Moore算法的思想和实现,这在面试中经常被考察。同时也要熟悉哈希表解法,因为它适用于更广泛的统计问题。

http://www.jsqmd.com/news/155443/

相关文章:

  • 刚调试完一个追剪项目,客户要求切刀必须精确咬合印刷包装袋的切口。这玩意儿玩的就是主轴和从轴的默契配合——主轴带着材料跑,从轴伺服得在正确时间点扑上去完成剪切
  • YOLO模型缓存刷新机制:主动推送更新而非等待过期
  • STL专项:priority_queue 优先队列(堆)
  • EMC的三大法宝②:接地(二)
  • YOLO模型冷启动DNS预解析:减少网络首次延迟
  • YOLO模型灰度发布期间的内部培训计划
  • YOLO模型灰度发布期间的客户支持渠道开通
  • 手把手拆解全自动上位机:C#多线程玩转西门子PLC
  • YOLO目标检测全流程拆解:数据标注到GPU部署的每一步
  • YOLO推理批处理优化:提升GPU利用率的秘密武器
  • Java常见技术分享-17-多线程安全-并发编程的核心问题的解决方案
  • 每天一个网络知识:什么是以太网虚拟专用网络?
  • 全国首批10城菁彩Vivid影厅启幕,《山河故人》重映见证影像新纪元
  • Java常见技术分享-18-多线程安全-进阶模块-并发集合与线程池
  • 以太网二层协议有哪些?
  • 【python大数据毕设实战】音乐内容智能推荐与市场趋势分析系统、Hadoop、计算机毕业设计、包括数据爬取、数据分析、数据可视化、机器学习、实战教学
  • Linux 入门必掌握的十大命令
  • 算法-回溯-14
  • 《创业之路》-761-《架构思维:从程序员到CTO》第四部 - 架构师的职业规划与能力成长:从执行者到战略引领者的跃迁,技术、业务与软技能的三角支撑。
  • YOLO与Prometheus Thanos Ruler集成:跨集群告警规则
  • YOLO与Kubeflow MLOps集成:端到端机器学习 pipeline
  • YOLO在噪音污染监测的应用:施工机械视觉识别
  • TCN-BiGRU回归+特征贡献SHAP分析+新数据预测+多输出,MATLAB代码
  • YOLO目标检测中的知识蒸馏实践:Teacher-Student架构
  • 《创业之路》-763-公司的组织架构服务于产品技术架构,技术架构服务于组织的业务服务,组织的业务服务于组织的战略,组织的战略服务于政府的规划、政府的规划服务于国家的战略。上下贯通,一脉相承,方为顺势。
  • 提示工程实战:如何用Prompt让游戏AI理解玩家的“隐藏需求”
  • 【毕业设计】基于SpringBoot的儿童医院挂号管理系统的设计与实现(源码+文档+远程调试,全bao定制等)
  • 推荐阅读:深入解析C语言编程中的指针与内存管理
  • YOLO模型训练资源使用趋势预测:基于历史数据分析
  • 事件委托(Event Delegation)