PHP面试题精讲—无限级分类/无限分类的递归算法和非递归算法-带源码建议收藏
Posted 雨果虾滑猫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP面试题精讲—无限级分类/无限分类的递归算法和非递归算法-带源码建议收藏相关的知识,希望对你有一定的参考价值。
日拱一卒无有尽,功不唐捐终入海 💋
经典问题-无限级分类
无限级分类,也被称为无限分类,如下图所示,他是一个非常经典的问题。
最经典的应用场景就是后台系统的菜单,包括但是不仅限于
- 省市区直辖市下拉菜单
- 信息系统后台管理的菜单管理
- 实际工作业务中的组织层级管理
那么无限级分类为什么经典呢?原因有三:
- 了解面试者是否会用递归算法
- 观察面试者处理实际问题的解决思路
- 根据实际代码看面试者对语言(php)的熟练程度
接下来,让我带你一层一层的分析问题,从中通过深度思考,学到更多的东西,这样不仅对于面试,并且对于以后的工作学习也是有帮助的。
问题描述
给出如下数组结构,输出返回层级菜单。
输入
$arr = [
['id'=>1,'pid'=>0,'name'=>'内容管理'],
['id'=>2,'pid'=>1,'name'=>'内容管理1'],
['id'=>3,'pid'=>1,'name'=>'内容管理2'],
['id'=>4,'pid'=>3,'name'=>'内容管理22'],
['id'=>5,'pid'=>0,'name'=>'附件管理'],
['id'=>6,'pid'=>5,'name'=>'内容管理'],
['id'=>7,'pid'=>6,'name'=>'内容管理1'],
['id'=>8,'pid'=>7,'name'=>'模块1'],
['id'=>9,'pid'=>8,'name'=>'模块11'],
['id'=>10,'pid'=>9,'name'=>'模块111'],
];
输出结果为按层级的数组
问题分析-为什么他是一个高频问题?
原因是通过这个问题的考察,他能够看到你解决问题的思路。
可以通过递归解决,通过递归的写法,面试官就能看出来你对递归条件的判断,什么时候跳出递归的界限是否清晰,函数调用是否合理。
解决问题
1.递归方法
递归,就是在运行的过程中调用自己。
递归可以说是分而治之的思想的体现,将一个问题分解成一个小问题,并且在判断范围内调用自己来解决一个问题,直到这个问题不再适用于自己的解决范围。
用最快的方式理解递归,只需要记住这几点
- 递归的界限在哪里结束
- 递归的调用在哪里执行
- 维护的结果是如何传递的
public function makeTree($array, $pid =0, $level = 0) //传递结果
{
static $list = [];
foreach ($array as $key => $value) {
if ($value['pid'] == $pid) { //递归的界限
$value['level'] = $level;
$value['name'] = str_repeat('-',$level).$value['name'];
$list[] = $value;
unset($array[$key]);
$this->makeTree($array, $value['id'], $level + 1); //递归的调用
}
}
return $list;
}
输出结果是一个按层级排列的一维数组,name
已经按层级增加了前缀,这样的数组可以直接给下拉菜单使用。
array (
0 =>
array (
'id' => 1,
'pid' => 0,
'name' => '内容管理',
'level' => 0,
),
1 =>
array (
'id' => 2,
'pid' => 1,
'name' => '-内容管理1',
'level' => 1,
),
2 =>
array (
'id' => 3,
'pid' => 2,
'name' => '--内容管理2',
'level' => 2,
),
3 =>
array (
'id' => 7,
'pid' => 3,
'name' => '---内容管理22',
'level' => 3,
),
4 =>
array (
'id' => 4,
'pid' => 7,
'name' => '----模块1',
'level' => 4,
),
5 =>
array (
'id' => 8,
'pid' => 0,
'name' => '附件管理',
'level' => 0,
),
6 =>
array (
'id' => 5,
'pid' => 8,
'name' => '-模块11',
'level' => 1,
),
7 =>
array (
'id' => 9,
'pid' => 5,
'name' => '--内容管理',
'level' => 2,
),
8 =>
array (
'id' => 6,
'pid' => 9,
'name' => '---模块111',
'level' => 3,
),
9 =>
array (
'id' => 10,
'pid' => 6,
'name' => '----内容管理1',
'level' => 4,
),
)
那么这个非常经典的递归方法,我们如何分析其中的问题呢?
这个调用的结束点在,找不到下一级为止,即当前元素的pid和下一次循环的pid都不一致
。
通过增加变量times可以发现,10个元素下函数调用次数为11次。
通过前后执行函数memory_get_usage()
,得出内存使用: 5.367188 kb
2.非递归方法
递归方法的缺点在于比较消耗资源,那么不用递归也是能解决这个问题的。
先给出源码
function makeTree($arr)
{
$pid = $arr[0]['pid'];
$arr_assert = [];
$arr_new = [];
foreach ($arr as $value) {
$arr_new[$value['id']] = $value;
if (!isset($arr_assert[$value['pid']])) {
$arr_assert[$value['pid']]['start'] = 0;
$arr_assert[$value['pid']]['end'] = 0;
}
$arr_assert[$value['pid']][] = $value['id'];
++$arr_assert[$value['pid']]['end'];
}
$result = [];
$count = count($arr_new);
$offset = 0;
while ($offset < $count) {
if (isset($arr_assert[$pid]) && $arr_assert[$pid]['start'] < $arr_assert[$pid]['end']) {
$id = $arr_assert[$pid]['start'];
++$arr_assert[$pid]['start'];
$pid = $arr_assert[$pid][$id];
$result[] = $arr_new[$pid];
++$offset;
} else
$pid = $arr_new[$pid]['pid'];
}
return $result;
}
这一步得出的结构是树形的结构
array (
0 =>
array (
'id' => 1,
'pid' => 0,
'name' => '内容管理',
),
1 =>
array (
'id' => 2,
'pid' => 1,
'name' => '内容管理1',
),
2 =>
array (
'id' => 3,
'pid' => 2,
'name' => '内容管理2',
),
3 =>
array (
'id' => 7,
'pid' => 3,
'name' => '内容管理22',
),
4 =>
array (
'id' => 4,
'pid' => 7,
'name' => '模块1',
),
5 =>
array (
'id' => 8,
'pid' => 0,
'name' => '附件管理',
),
6 =>
array (
'id' => 5,
'pid' => 8,
'name' => '模块11',
),
7 =>
array (
'id' => 9,
'pid' => 5,
'name' => '内容管理',
),
8 =>
array (
'id' => 6,
'pid' => 9,
'name' => '模块111',
),
9 =>
array (
'id' => 10,
'pid' => 6,
'name' => '内容管理1',
),
)
总结
对于无限级分类这个问题,由于在实际项目应用非常广泛,所以这是一个概率非常大会出现的面试题。
以上是关于PHP面试题精讲—无限级分类/无限分类的递归算法和非递归算法-带源码建议收藏的主要内容,如果未能解决你的问题,请参考以下文章
PHP利用递归函数实现无限级分类的方法_php技巧 - PHP