如何在PHP中打印每个子节点和孙子节点旁边的最高级父节点

时间:2016-09-09 04:46:32

标签: php mysql excel vba algorithm

我有一个PHP代码,它允许我创建一个映射有子节点和子节点的父节点数组。这是我使用的代码:

<?php
function parent_map( &$a, $parent_key, $children_key )
{
    $orphans = true; $i;
    while( $orphans )
    {
        $orphans = false;
        foreach( $a as $k=>$v )
        {
            // is there $a[$k] sons?
            $sons = false;
            foreach( $a as $x=>$y )
            if( isset($y[$parent_key]) and $y[$parent_key]!=false and $y[$parent_key]==$k )  
            { 
                $sons=true; 
                $orphans=true; 
                break;
            }

            // $a[$k] is a son, without children, so i can move it
            if( !$sons and isset($v[$parent_key]) and $v[$parent_key]!=false )
            {
                $a[$v[$parent_key]][$children_key][$k] = $v;
                unset( $a[$k] );
            }
        }
    }
}

$ARRAY = array(
    1 => array( 'label' => "A" ),
    2 => array( 'label' => "B" ),
    3 => array( 'label' => "C" ),
    4 => array( 'label' => "D" ),
    5 => array( 'label' => "one", 'father' => '1' ),
    6 => array( 'label' => "two", 'father' => '1' ),
    7 => array( 'label' => "three", 'father' => '1' ),
    8 => array( 'label' => "node 1", 'father' => '2' ),
    9 => array( 'label' => "node 2", 'father' => '2' ),
    10 => array( 'label' => "node 3", 'father' => '2' ),
    11 => array( 'label' => "I", 'father' => '9' ),
    12 => array( 'label' => "II", 'father' => '9' ),
    13 => array( 'label' => "III", 'father' => '9' ),
    14 => array( 'label' => "IV", 'father' => '9' ),
    15 => array( 'label' => "V", 'father' => '9' ),
);

parent_map( $ARRAY, 'father', 'children' );

echo "<pre>"; print_r( $ARRAY);


?>

这能够为我提供父节点及其子节点和子节点的子节点等的树结构。

我需要的是从这种形式转换的数据:

Parent Child

AAA   BBB
AAA   CCC
AAA   DDD
BBB   EEE
BBB   FFF
CCC   GGG
FFF   HHH
III   JJJ
JJJ   KKK
JJJ   LLL

到这种形式:

Node    1st Level Node
AAA     Root
BBB     AAA
CCC     AAA
DDD     AAA
EEE     AAA
FFF     AAA
GGG     AAA
HHH     AAA
III     Root
JJJ     III
KKK     III
LLL     III

本质上我希望用各自最高级别的根节点填充子节点,这样所有子节点都有一个映射到它们的根级数据/父节点。

我尝试在excel中使用VBA,但它可以在大型数据集中挂起。一个解决方案是立即获取所有数据并一起写出,但我不是在查看宏解决方案。

这是我的VBA代码:

Option Explicit
Sub Main_Function_SuperManager()
Dim i, re
Root_Parent
Replace
Replace_Name
i = 1
    While Cells(i, 22) <> ""

         Cells(i, 22) = ""
         Cells(i, 23) = ""
         i = i + 1

    Wend
End Sub
Sub Root_Parent()
    Dim i, re, k
    i = 2
    While Cells(i, 1) <> ""
        Set re = Range("B:B").Find(Cells(i, 1))
        If re Is Nothing Then
            Set re = Range("V:V").Find(Cells(i, 1))
            If re Is Nothing Then
                k = k + 1
                Cells(k, 22) = Cells(i, 1)
                Cells(k, 23) = "Super Manager"
                findchild Cells(k, 22).Value, k
            End If
        End If
        i = i + 1
    Wend
End Sub
Sub findchild(parent, ByRef k)
 Dim i, s, re
 i = 1
    While Cells(i, 2) <> ""
    s = i
        Do
            Set re = Range("B:B").Find(Cells(s, 1))
            If re Is Nothing Then
                If Cells(s, 1) = parent Then
                k = k + 1
                Cells(k, 22) = Cells(i, 2)
                Cells(k, 23) = Cells(s, 1)
                End If
                Exit Do
            Else
                s = re.Row
            End If
        Loop
        i = i + 1
    Wend
End Sub

Sub Replace()
    Dim i, re, s
    i = 2
    While Cells(i, 22) <> ""
        Set re = Range("B:B").Find(Cells(i, 22))
        If re Is Nothing Then
        Cells(10, 24) = ""
        Else
         s = re.Row
         Cells(s, 19) = Cells(i, 23)
        End If
        i = i + 1

    Wend
End Sub

Sub Replace_Name()
    Dim i, re, s
    i = 2
    While Cells(i, 19) <> ""
        Set re = Worksheets("Sheet2").Range("A:A").Find(Cells(i, 19))
        If re Is Nothing Then
        Cells(10, 24) = ""
        Else
         s = re.Row
         Cells(i, 20) = Worksheets("Sheet2").Cells(s, 2)
        End If
        i = i + 1

    Wend
End Sub

我希望我可以将相同的功能转换为PHP。我将从数据库填充数组,但如何创建一个唯一的ID /子列表到他们的最高级父/超级父节点是我无法弄清楚的。

期待就此提出建议。提前谢谢。

2 个答案:

答案 0 :(得分:0)

您应该使用Disjoint-set数据结构。此数据结构中的每个节点都包含对其父节点的引用,每个节点将由其root节点表示。如果您使用此算法以及路径压缩改进,最终的父数组将完全符合您的要求。

对于这个问题,你应该首先初始化父数组,如下所示:

parent[A] = A
parent[B] = B
...

然后编写find函数,该函数以递归方式查找每个集合的根,并将其指定为路径中所有节点的父节点:

function find(x)
    if parent[x] == x: // x is root
        return x
    root = find(parent[x]) 
    parent[x] = root

下一步是处理边缘

for each U->V
    parent[V] = find(U) // finds the root of the tree that U belongs to
                        // and assign that as parent of V

A->B, find(A)=A, parent[B] <- A
A->C, find(A)=A, parent[C] <- A
A->D, find(A)=A, parent[D] <- A
B->E, find(B)=A, parent[E] <- A
B->F, find(B)=A, parent[F] <- A
C->G, find(C)=A, parent[G] <- A
F->H, find(C)=A, parent[F] <- H
I->J, find(I)=I, parent[J] <- I
J->K, find(J)=I, parent[K] <- J
J->L, find(J)=I, parent[L] <- J

答案 1 :(得分:0)

  

改变表格应该如下所示

CREATE TABLE `parent_child` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `parent` varchar(255) DEFAULT NULL,
  `child` varchar(255) DEFAULT NULL,
  `parent_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1
  

创建获取父子元素的函数

DELIMITER $$

USE `yourdatabase`$$

DROP FUNCTION IF EXISTS `GetAllNode1`$$

CREATE DEFINER=`root`@`localhost` FUNCTION `GetAllNode1`(GivenID INT) RETURNS TEXT CHARSET latin1
    DETERMINISTIC
BEGIN
    DECLARE rv,q,queue,queue_children TEXT;
    DECLARE queue_length,front_id,pos INT;
    SET rv = '';
    SET queue = GivenID;
    SET queue_length = 1;
    WHILE queue_length > 0 DO
        SET front_id = queue;
        IF queue_length = 1 THEN
            SET queue = '';
        ELSE
            SET pos = LOCATE(',',queue) + 1;
            SET q = SUBSTR(queue,pos);
            SET queue = q;
        END IF;
        SET queue_length = queue_length - 1;
        SELECT IFNULL(qc,'') INTO queue_children
        FROM (SELECT GROUP_CONCAT(id) AS qc
        FROM `parent_child` WHERE `parent_id` = front_id) A ;
        IF LENGTH(queue_children) = 0 THEN
            IF LENGTH(queue) = 0 THEN
                SET queue_length = 0;
            END IF;
        ELSE
            IF LENGTH(rv) = 0 THEN
                SET rv = queue_children;
            ELSE
                SET rv = CONCAT(rv,',',queue_children);
            END IF;
            IF LENGTH(queue) = 0 THEN
                SET queue = queue_children;
            ELSE
                SET queue = CONCAT(queue,',',queue_children);
            END IF;
            SET queue_length = LENGTH(queue) - LENGTH(REPLACE(queue,',','')) + 1;
        END IF;
    END WHILE;
    RETURN rv;
END$$

DELIMITER ;

为欲望输出编写查询

SELECT GetAllNode1(id) FROM parent_child 
or 
SELECT GetAllNode1(id) FROM parent_child  where id =1 //for specific parent's child element