我有一个具有分层结构的数组。在这个例子中,第一级有两个元素,83和82.82有子84和87。 87岁是84岁的孩子。
Array
(
[_children] => Array
(
[0] => Array
(
[id] => 11
[uid] => 24
[rid] => 83
[date] => 2011-06-18 15:08:10
)
[1] => Array
(
[id] => 10
[uid] => 24
[rid] => 82
[date] => 2011-06-18 15:08:07
[_children] => Array
(
[0] => Array
(
[id] => 12
[uid] => 82
[rid] => 84
[date] => 2011-06-18 15:30:34
[_children] => Array
(
[0] => Array
(
[id] => 13
[uid] => 84
[rid] => 87
[date] => 2011-06-18 16:11:50
)
)
)
)
)
)
)
我想循环遍历该数组,并为另一个数组中的每个级别存储一些信息。这就是我的数组在最后的样子。
Array
(
[0] => Array
(
[elements] => 2
)
[1] => Array
(
[elements] => 1
)
)
E.g。这表明在0级是3个节点,在1级有5个节点。我怎样才能做到这一点?有什么想法吗?
答案 0 :(得分:2)
我只计算数字。
这意味着基本密钥的每个项目加上它的子项相同。
与递归相反,我决定使用堆栈。
要获得每个级别的值,计数器还需要一个级别上下文,它将添加到节点旁边的堆栈中:
$count = array();
$stack[] = array(-1, $arr);
while($item = array_shift($stack)) {
list($level, $node) = $item;
$count[$level] = isset($count[$level]) ? $count[$level]+1 : 1;
if (!isset($node['_children']))
continue;
foreach($node['_children'] as $item)
$stack[] = array($level+1, $item);
}
var_dump($count);
这确实输出:
array(4) {
[-1]=> int(1)
[0] => int(2)
[1] => int(1)
[2] => int(1)
}
其中-1
是仅包含子节点的根节点。因此,您可能希望在处理后将其删除:
array_shift($count);
以下代码也为每个级别添加了一个节点收集器,称为$nodes
。根据{{1}} count($nodes[$level])
,它有点多余,但它只是为了展示如何收集节点:
$count[$level]
使用堆栈可以防止进行递归函数调用并降低复杂性。它背后的想法很简单:
这可确保以最小的开销计算每个节点。消耗堆栈的策略可以完全控制。 FIFO(First In, First Out),LIFO(Last In, First Out) - 易于实现 - 甚至是加权策略(第一个进程子节点没有/大多数孩子快速消耗它们)甚至随机堆栈消耗以分配数据结构意味着整个堆栈的约束。
使用自己的堆栈与PHP的函数堆栈进行比较首先表明两种方式都有共同之处。两者都使用堆栈。
但主要区别在于,自己的堆栈会阻止使用函数调用。调用函数,尤其是递归函数,可以避免多种含义。
另一个关键的区别是,自己的堆栈总是允许查询它,而没有对PHP语言自己的堆栈的控制。
接下来,可以按顺序处理自己的堆栈,而函数堆栈是一个递归构造,它将数据处理1:1映射到程序流程中。
虽然递归会隐藏函数内的代码的数据(树)的递归性质(可以简化处理),但是需要提供被隐藏的状态函数调用作为函数参数引入。
因此,管理解决问题的数据并不少,但与递归相同或更复杂。
因此,在函数调用的开销旁边,人们已经闻到了数据的开销。
根据PHP语言本身来看,这是值得的。
为了使递归工作,PHP需要在函数堆栈中添加其他变量表。需要在每次函数调用之前和之后管理这些表。
递归还需要您通过引用传递变量。这是一个额外的开销,因为这些值需要在PHP函数结束之前和之后设置/处理。
在递归中需要这样的引用来共享计数器的数据结构。递归实现中的这种开销可以通过在PHP中实现递归到它自己的类来减少,这在目前为止尚未被提出并且可能值得考虑。
与用户堆栈实现中的计数器数组一样,这样的类可以在所有函数调用之间共享计数器,从而提供类的成员函数可访问的散列,而不使用变量引用。此外,解决问题的复杂性可以在类中分布在不同的函数上,甚至作为依赖项注入。 PHP已经提供了默认的recursive iterator interface。
另一个减少一个地方的地方可以使用全局变量表($count = array();
$nodes = array(); // nodes per level go here.
$stack[] = array(-1, $arr);
while($item = array_shift($stack)) {
list($level, $node) = $item;
$count[$level] = isset($count[$level]) ? $count[$level]+1 : 1;
$nodes[$level][] = $node; // set here!
if (!isset($node['_children']))
continue;
foreach($node['_children'] as $item)
$stack[] = array($level+1, $item);
}
var_dump($count, $nodes);
),但其缺点是使代码不再可用,这标志着设计缺陷。即使设计不是问题,也需要在函数调用之间管理所谓的superglobals数组。这导致了一个非常类似的行为,通过引用传递它被认为是防止它。因此,这不是一个更好的解决方案。
这是一个基于上面讨论中提出的想法的递归实现,以获得更多的关注:
$GLOBALS
它的输出:
/**
* class implementation of a recursive child counter
*
* Usage:
*
* Object use:
*
* $counter = new LevelChildCounter($arr, '_children');
* $count = $counter->countPerLevel();
*
* Functional use:
*
* $count = LevelChildCounter::count($arr, '_children');
*/
class LevelChildCounter {
private $tree;
private $childKey;
private $counter;
public function __construct(array $tree, $childKey) {
$this->tree = $tree;
$this->childKey = $childKey;
}
/**
* functional interface of countPerLevel
*/
public static function count(array $tree, $childKey) {
$counter = new self($tree, $childKey);
return $counter->countPerLevel();
}
private function countUp($level) {
isset($this->counter[$level])
? $this->counter[$level]++
: $this->counter[$level] = 1;
}
private function countNode($node, $level) {
// count node
$this->countUp($level);
// children to handle?
if (!isset($node[$this->childKey]))
return;
// recursively count child nodes
foreach($node[$this->childKey] as $childNode)
$this->countNode($childNode, $level+1)
;
}
/**
* Count all nodes per level
*
* @return array
*/
public function countPerLevel() {
$this->counter = array();
$this->countNode($this->tree, -1);
return $this->counter;
}
}
$count = LevelChildCounter::count($arr, '_children');
array_shift($count);
var_dump($count);
答案 1 :(得分:1)
似乎递归函数是实现它的最简单方法。
该函数应该具有_children
数组作为参数,并且应该保留结果,“结果”数组和级别计数器。
该函数应该像getStructure($myarray['_children'])
一样调用,它调用基函数_getStructure($array, &$results, $depth)
:$results
首先是一个空数组,$depth
设置为零。
然后函数_getStructure
遍历数组(foreach
)并增加$results[$depth]++
。如果子项包含_children
字段,则此函数会再次调用_getStructure
,并增加$detph++
。
_getStructure
返回后,$results
数组将填充您的给定结构。这是由于引用传递,它会将$results
数组传递给函数而不复制它:对它的所有更改都会被调用它的作用域看到。请参阅PHP docs。
答案 2 :(得分:1)
我假设你有一个固定的深度,将它调整到无限深度是非常简单的:
<?php
function count_child( $node, &$results, $depth, $curr_depth = 0){
if( $curr_depth >= $depth)
return;
if( is_set( $results[$curr_depth] ))
$results[$curr_depth] += count( $node['_children'] );
else
$results[$curr_depth] = 1;
if( is_array( $node ) && is_set( $node['_children']) )
foreach( $node['_children'] as $next_node){
count_child( $next_node, $result , $depth , $curr_depth + 1);
}
}
?>