那么,
问题
假设我们有一个具有以下结构的扁平数组:
$array = [
['level'=>1, 'name' => 'Root #1'],
['level'=>1, 'name' => 'Root #2'],
['level'=>2, 'name' => 'subroot 2-1'],
['level'=>3, 'name' => '__subroot 2-1/1'],
['level'=>2, 'name' => 'subroot 2-2'],
['level'=>1, 'name' => 'Root #3']
];
问题是 - 要将该数组转换为树形结构。层次关系仅由元素顺序和“level”字段决定。让我们将“children”定义为存储子节点的维度名称。对于上述数组,这将是:
array ( array ( 'level' => 1, 'name' => 'Root #1', ), array ( 'level' => 1, 'name' => 'Root #2', 'children' => array ( array ( 'level' => 2, 'name' => 'subroot 2-1', 'children' => array ( array ( 'level' => 3, 'name' => '__subroot 2-1/1', ), ), ), array ( 'level' => 2, 'name' => 'subroot 2-2', ), ), ), array ( 'level' => 1, 'name' => 'Root #3', ), )更多解释:如果不明确谁是谁的父节点,请看以下代码,它可以很容易地展示这个思路:
function visualize($array)
{
foreach($array as $item)
{
echo(str_repeat('-', $item['level']).'['.$item['name'].']'.PHP_EOL);
}
}
visualize($array);
-对于上面的数组,它是这样的:
-[根 #1] -[根 #2] --[子根 2-1] ---[__子子根 2-1/1] --[子根 2-2] -[根 #3]
具体要求
对于期望解决方案和输入数组都有一些限制:
- 输入数组总是有效的:这意味着其结构始终可以重构为树形结构。没有负数/非数字级别,没有无效的级别结构等奇怪的事情。
- 输入数组可能很大,目前最大级别没有限制
- 解决方案必须使用单个循环来解决问题,因此我们不能将数组拆分为块,应用递归或以某种方式跳转到数组中。只需简单的
foreach
(或另一个循环-无关紧要),每个元素依次处理即可。
我的方法
目前,我使用堆栈实现了解决方案。我正在使用引用并维护堆栈的当前元素,在当前步骤中将写入该元素。也就是说:
function getTree(&$array)
{
$level = 0;
$tree = [];
$stack = [&$tree];
foreach($array as $item)
{
if($item['level']>$level) //expand stack for new items
{
//if there are child elements, add last to stack:
$top = key($stack);
if(count($stack[$top]))
{
end($stack[$top]);
$stack[] = &$stack[$top][key($stack[$top])];
}
//add ['children'] dim to top stack element
end($stack);
$top = key($stack);
$stack[$top]['children'] = [];
$stack[] = &$stack[$top]['children'];
}
while($item['level']<$level--) //pop till certain level
{
//two times: one for last pointer, one for ['children'] dim
array_pop($stack);
array_pop($stack);
}
//add item to stack top:
end($stack);
$stack[key($stack)][] = $item;
$level = $item['level'];
}
return $tree;
}
-既然内容很长,我已经创建了一个使用和输出示例。
问题
正如您所看到的,我的解决方案相当冗长,并且依赖于引用和数组内部指针处理(例如 end()
等),因此问题是:
可能有其他更短,更清晰的方法来解决这个问题吗? 看起来像一些标准问题,但我没有找到任何相应的解决方案(有一个类似的问题 - 但在那里 OP 具有确切的parent_id
隶属性而我没有)
1E6
迭代计数和数组方面都具有相同的时间,这是在问题中指定的(我的版本总共70秒
,而你的版本为74.5秒
)。我还在简单的数组上进行了测试(所有级别都为1
),并获得了类似的结果(我的版本为34秒
,而你的版本为39秒
)。这是预期的,因为两个函数具有相同的大O估计。 - Alma Do