计数数组法可在O(n)时间内找出众数,适用于值域集中且可控的整型数组;通过映射元素到索引累加频次,再扫描计数数组获取最大频次对应值,支持负数偏移与多众数扩展。

在 Java 中,若数组元素范围已知且较集中(如非负整数、取值在 [0, k] 之间),可借助计数数组(count array)在 O(n) 时间内找出众数(出现次数最多的元素)。该方法本质是空间换时间,避免排序或哈希表的常数开销,适合整型数组且值域可控的场景。
适用前提:明确值域范围
计数数组法要求能预估数组中元素的最小值和最大值。最常见的是元素为非负整数且最大值不超过某个合理上限(例如 ≤ 10⁵)。若值域过大(如 int 范围全量)或含负数/浮点数,则需先离散化或改用 HashMap。
- 若所有元素 ∈ [min, max],则申请长度为 max - min + 1 的 int 数组
- 遍历原数组时,将每个元素
x映射到索引x - min处进行累加 - 最后扫描计数数组,找到最大计数值对应的索引,再还原为原始值
核心实现步骤(含边界处理)
以非负整数为例(min = 0),假设输入数组为 int[] nums:
- 先遍历一次,获取最大值
maxVal,确定计数数组长度:new int[maxVal + 1] - 第二次遍历
nums,对每个num执行:count[num]++ - 第三次遍历
count数组,记录最大频次及其下标:if (count[i] > maxCount) { maxCount = count[i]; mode = i; } - 返回
mode即为众数(若多个元素并列最多,此法默认返回值最小的那个;如需全部众数,可额外收集)
处理负数与多众数的扩展方式
若数组含负数,例如范围在 [-50, 50],可统一偏移:令 offset = 50,则索引映射为 num + offset,计数数组长度为 101。若需返回所有众数,不提前终止,而是在第三步中维护一个 List,每当 count[i] == maxCount 时加入,遇到更大值则清空并更新。
对比其他方法的取舍
排序法(O(n log n))或 HashMap(平均 O(n),但有哈希计算与扩容开销)更通用;而计数数组法在满足值域约束时,真正严格线性、无装箱、缓存友好。实际编码中,建议先校验数据分布——若最大值远超数组长度(如 [0, 10⁶] 中仅 100 个数),空间浪费大,此时 HashMap 更稳妥。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











