在PHP开发中,处理数组相关需求是非常常见的场景,其中求数组中的众数也是经常会遇到的需求。众数指的是在一组数据中出现次数最多的数值,如果多个数值出现次数相同且都是最高,那么这些数值都属于众数。

什么是数组众数
数组众数就是数组中所有元素出现频次最高的那个或者那些元素。比如数组[1,2,2,3,3,4]中,2和3都出现了2次,是出现次数最多的,所以这个数组的众数是2和3。如果数组是[1,2,3,4],每个元素都只出现1次,那么所有元素都是众数。
基础遍历统计实现
最直观的思路就是先遍历数组,统计每个元素出现的次数,然后再找出出现次数最高的元素。具体实现步骤如下:
- 创建一个空数组用来存储每个元素的出现次数
- 遍历原数组,对每个元素进行计数
- 找出计数数组中的最大值
- 遍历计数数组,把计数等于最大值的元素收集起来就是众数
下面是完整的实现代码:
<?php
/**
* 基础遍历法求数组众数
* @param array $arr 待处理的数组
* @return array 众数组成的数组
*/
function getModeByTraverse($arr) {
if (empty($arr)) {
return [];
}
$countMap = [];
// 统计每个元素的出现次数
foreach ($arr as $item) {
if (!isset($countMap[$item])) {
$countMap[$item] = 0;
}
$countMap[$item]++;
}
// 找出最大的出现次数
$maxCount = max($countMap);
// 收集所有出现次数等于最大值的元素
$modes = [];
foreach ($countMap as $key => $count) {
if ($count == $maxCount) {
$modes[] = $key;
}
}
return $modes;
}
// 测试示例
$testArr1 = [1, 2, 2, 3, 3, 4];
$testArr2 = [1, 2, 3, 4];
$testArr3 = ['a', 'b', 'b', 'c', 'c', 'c'];
var_dump(getModeByTraverse($testArr1)); // 输出 array(2) { [0]=> int(2) [1]=> int(3) }
var_dump(getModeByTraverse($testArr2)); // 输出 array(4) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> int(4) }
var_dump(getModeByTraverse($testArr3)); // 输出 array(1) { [0]=> string(1) "c" }
?>
使用内置函数优化实现
PHP提供了array_count_values函数,可以直接统计数组中所有值出现的次数,利用这个函数可以简化统计步骤,让代码更简洁。
实现思路如下:
- 使用
array_count_values直接得到每个元素的计数数组 - 找出计数数组的最大值
- 使用
array_keys获取计数等于最大值的元素
完整代码如下:
<?php
/**
* 使用内置函数求数组众数
* @param array $arr 待处理的数组
* @return array 众数组成的数组
*/
function getModeByBuiltin($arr) {
if (empty($arr)) {
return [];
}
// 统计每个元素的出现次数
$countMap = array_count_values($arr);
// 找出最大的出现次数
$maxCount = max($countMap);
// 获取所有出现次数等于最大值的元素键名
$modes = array_keys($countMap, $maxCount, true);
return $modes;
}
// 测试示例
$testArr1 = [1, 2, 2, 3, 3, 4];
$testArr2 = [1, 2, 3, 4];
$testArr3 = ['a', 'b', 'b', 'c', 'c', 'c'];
var_dump(getModeByBuiltin($testArr1)); // 输出 array(2) { [0]=> int(2) [1]=> int(3) }
var_dump(getModeByBuiltin($testArr2)); // 输出 array(4) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> int(4) }
var_dump(getModeByBuiltin($testArr3)); // 输出 array(1) { [0]=> string(1) "c" }
?>
两种实现方式的对比
我们可以通过下面的表格对比两种实现方式的特点:
| 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 基础遍历法 | 逻辑清晰,不依赖特定内置函数,兼容性好 | 代码相对较长,需要手动处理计数逻辑 | 需要自定义计数逻辑,或者对代码逻辑有明确要求的环境 |
| 内置函数法 | 代码简洁,开发效率高,利用PHP原生函数性能有保障 | 依赖array_count_values函数,对元素类型有要求(元素需要是integer或者string类型) | 常规数组众数统计,数组元素类型符合要求的场景 |
注意事项
在使用上述方法的时候,需要注意几个问题:
- 如果数组中包含非integer和非string类型的元素,使用
array_count_values函数会报错,这种情况下建议使用基础遍历法,并且可以根据需求调整计数逻辑 - 如果数组为空,上述两个函数都会返回空数组,调用的时候可以根据需要处理空数组的情况
- 如果数组中的元素是浮点型,基础遍历法可以正常统计,但是
array_count_values会报错,因为浮点型不能作为数组的键名
下面是处理浮点型数组的示例:
<?php
/**
* 支持浮点型的众数统计
* @param array $arr 待处理的数组
* @return array 众数组成的数组
*/
function getModeWithFloat($arr) {
if (empty($arr)) {
return [];
}
$countMap = [];
foreach ($arr as $item) {
// 将浮点型转为字符串作为键名,避免精度问题
$key = is_float($item) ? (string)$item : $item;
if (!isset($countMap[$key])) {
$countMap[$key] = 0;
}
$countMap[$key]++;
}
$maxCount = max($countMap);
$modes = [];
foreach ($countMap as $key => $count) {
if ($count == $maxCount) {
// 如果是浮点型转换的字符串,转回浮点型
$modes[] = is_numeric($key) && strpos($key, '.') !== false ? (float)$key : $key;
}
}
return $modes;
}
// 测试浮点型数组
$testFloatArr = [1.2, 2.3, 2.3, 3.4, 3.4, 3.4];
var_dump(getModeWithFloat($testFloatArr)); // 输出 array(1) { [0]=> float(3.4) }
?>