计算相邻数组中的元素

时间:2011-06-18 16:16:33

标签: php arrays

我有一个具有分层结构的数组。在这个例子中,第一级有两个元素,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个节点。我怎样才能做到这一点?有什么想法吗?

3 个答案:

答案 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]

讨论

使用堆栈可以防止进行递归函数调用并降低复杂性。它背后的想法很简单:

  1. 使用第一个节点(仅包含子节点)初始化堆栈 - 树的根节点。
  2. 堆栈中的条目包含要计数的节点及其级别
  3. 堆栈中的每个元素都被视为相同:
    1. 项目从堆栈的开头(或结尾)移除。
    2. 它的等级计数器增加了一个。
    3. 如果它有子节点,则每个子节点都会以其级别添加到堆栈中。
  4. 将处理堆栈,直到消耗掉所有元素。
  5. 这可确保以最小的开销计算每个节点。消耗堆栈的策略可以完全控制。 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);
        } 

}
?>