通过在PHP中遍历数组来构建树

时间:2012-11-03 08:58:37

标签: php arrays tree

如何在PHP中使用数组构建树,知道构建树时需要遵循的规则?

我在代码中没有任何特别的地方,我可以找到真正的错误 无论如何,问题可能与我反复使用的参考传递有关 我没有使用递归,因为速度真的真的很重要。从输入数组的规则(我完全控制它们),这是可能的,更快,没有递归。

工作原理: 当您横向数组时,每个元素的['start_order']比前一个['start_order']的数字更大。
每次下一个元素的['start_order']大于此元素的''start_order'时]并且下一个元素的['end_order']小于此元素的['end_order'],然后该元素是此元素的子元素
在该步骤之后,我必须找到所有元素那个元素的孩子(我刚发现它就是这个元素的孩子)。

这是我的代码。

<?php
ksort($treeArray);
$tree = array();
$stack = array();
reset($treeArray);
$currentParent = &$treeArray[key($treeArray)];
$tree[] = &$treeArray[key($treeArray)];

while(next($treeArray) !== false){

    if(current($treeArray)['start_order'] <= $currentParent['end_order']){
        if(current($treeArray)['end_order'] <= $currentParent['end_order']){
            // There's a child of the previous

            // push the new parent
            $stack[] = $currentParent;

            if(!isset($currentParent['children'])){
                $currentParent['children'] = array();
            }
            $currentParent['children'][] = &$treeArray[key($treeArray)];
            $currentParent = current($treeArray);

        }else{
            // Supposed not to happen. Log the problem.
        }
    }else /* if(current($treeArray)['start_order'] > $currentParent['end_order']) */{
        // Pop the child here, there are no more children.
        if(($popedElement = array_pop($stack)) === NULL){
            $tree[] = $currentParent;
        }else{
            $popedElement['children'][] = &$treeArray[key($treeArray)];
            $stack[] = $popedElement;
            $currentParent = current($treeArray);
        }
    }
}

?>

示例:

这个输入数组可以是结构上看起来像这样的东西:

[1][child1][child1child1][child2][child2child1][child2child2][child2child3][2][child2child1]

解析为此树:

[1]
    [child1]
        [child1child1]
    [child2]
        [child2child1]
        [child2child2]
        [child2child3]
[2]
    [child2child1]

不要忘记这个命令保持不变。 [child2child3]永远不会出现在[child2child2]之前,[2]永远不会出现在[1]之前。在这个类比中,几乎就像处理XML一样。

1 个答案:

答案 0 :(得分:0)

在这里发现问题。问题与我在尝试解决此问题时如何处理传递引用有关。

以下是解决方案:

$tree[] = &$treeArray[key($treeArray)];
$currentParent = &$treeArray[key($treeArray)];
next($treeArray);

while(current($treeArray) !== false){

    if(current($treeArray)['start_order']['start_position'] <= $currentParent['end_order']['end_order']){
        if(current($treeArray)['end_order']['end_order'] <= $currentParent['end_order']['end_order']){
            // There's a child of the previous

            // push the new parent
            $stack[] = &$currentParent;

            $currentParent['children'][] = &$treeArray[key($treeArray)];

            $currentParent = &$treeArray[key($treeArray)];
        }else{
            // Supposed not to happen. Log the problem.
        }
        next($treeArray);
    }else /* if(current($treeArray)['start_order']['start_position'] > $currentParent['end_order']['end_order']) */{
        // Close previous element here. There are no more children.

        if(end($stack) === false){
            $currentParent = &$treeArray[key($treeArray)];
            $tree[] = &$treeArray[key($treeArray)];

            next($treeArray);
        }else{              
            $currentParent = &$stack[key($stack)];
            unset($stack[key($stack)]);
        }
    }
}

主要问题实际上是PHP中的传递引用,它与C中的不同。
为了解决这个问题,我无法同时使用push或pop php函数:
推送是因为$ var []运算符更快并且使用该函数 pop,因为它的返回是我之前推送的内容的副本,而不是我推送的实际元素。

此外,所有变量赋值必须通过引用(使用&amp;字符)显式地进行,因此使用current()进行赋值是不可能的,而是我需要像我一样使用key()函数。

我还有一个小而重要的错误,迫使我改变“while”指令。它现在包含current()而不是next()。这只是因为在堆栈弹出后我不能移动到下一个元素。在转到下一个之前,我需要再做一次迭代。这解决了当多个标签在同一个地方关闭时生成的错误嵌套。

请注意,此代码未针对速度进行优化