Php 从一组父子关系创建树结构

Php 从一组父子关系创建树结构,php,arrays,Php,Arrays,我有一个数组,它保存图形中节点之间的父子关系。每个嵌套数组的形式如下 array( 0 => parent_node_id, 1 => child_node_id ) 所以在这个数组中: 0 => array( 0 => 1 1 => 3 ) 这两个节点是1和3,节点1和节点3之间存在父子关系(外部数组索引0不相关) 表示节点3和节点5之间的父子关系(与1无关) 下面是父子关系数组(请注意,外部数组(0、1、2、3等)的数组索引不表示任何内容): 以

我有一个数组,它保存图形中节点之间的父子关系。每个嵌套数组的形式如下

array( 0 => parent_node_id, 1 => child_node_id )
所以在这个数组中:

0 => array(
   0 => 1
   1 => 3
)
这两个节点是1和3,节点1和节点3之间存在父子关系(外部数组索引
0
不相关)

表示节点3和节点5之间的父子关系(与
1
无关)

下面是父子关系数组(请注意,外部数组(0、1、2、3等)的数组索引不表示任何内容):

以下是其编码的数据结构的图示:

并且以代码格式(尽管对于数组结构有任何更好的想法,我可以在以后生成HTML列表,我将不胜感激!)

使用这个数组中的信息,我想生成一个树,然后用它在html页面中构建菜单。如何仅使用我的父子关系数组来实现这一点


我知道有许多类似的算法可用于堆栈溢出,但没有一种适用于多个根或我正在使用的特定数组输入结构。

OP在评论中指出,这将用于构建菜单系统,因此,我编写了一些代码,将数组数组转换为更易于理解的数据结构,构建嵌套列表(适合用作菜单),并使用第二个
$content
数组中的数据填充列表项。实际上,
$content
中的数据可以是一组链接或任何需要的内容

# input array
$array = array(
0 => array(
   0 => 1,
   1 => 3
),
1 => array(
   0 => 3,
   1 => 5
),
2 => array(
   0 => 3,
   1 => 7
),
3 => array(
   0 => 3,
   1 => 9
),
4 => array(
   0 => 1,
   1 => 10
),
5 => array(
   0 => 10,
   1 => 15
));

# associative array of node IDs and the node contents
# obviously for a menu, the node contents will probably be links
$content = array(
  1 => 'ape',
  3 => 'bear',
  5 => 'cow',
  7 => 'dog',
  9 => 'elephant',
  10 => 'frog',
  15 => 'giraffe'
);

$tree = [];

# simplify the parent-child 
foreach (array_values($array) as $a) {
  $tree[ $a[0] ][ $a[1] ] = 'value';
}

$roots = get_root($array);

# start our initial list...    
$str = '<ul>';
foreach (array_keys($roots) as $r) {
  $str .= build_tree( $tree, $content, $r );
}
$str .= "</ul>";
echo $str;

/**
 * build_tree($tree, $content, $n)
 * 
 * builds an html representation of a tree using $tree, a data structure
 * containing parent-child relationships, $content, the content of each
 * node of the tree, and $n, the current node in the tree. Calls itself
 * recursively when it encounters a subtree to be built
 *
 * @param array $tree 
 * @param array $content - assoc array of 
 * @param string $n - current node in the tree
 * @return string $html representing the tree
 */
function build_tree($tree, $content, $n) {
  $html = "<li>node id: $n; ".$content[$n]."</li>";
  # does $n exist in $tree -- i.e. does it have a child?
  if ( isset($tree[$n]) ) {
    # if so, start a new nested list
    $html .= '<li><ul>';
    # for each of the children of $n, our parent node,
    # run the build_tree code to create the html
    foreach (array_keys($tree[$n]) as $node) {
      $html .= build_tree($tree, $content, $node);
    }
    $html .= '</ul></li>';
  }
  return $html;
}

/**
 * get_root ( $input )
 *
 * input array format:
 * 0 => [ 0 => parent_node_id, 1 => child_node_id ],
 * 1 => [ 0 => parent_node_id2, 1 => child_node_id2 ],;
 *
 * takes an associative array of parent-child relationships
 * and makes two arrays, one containing all the parent nodes,
 * and one containing the child nodes. The root nodes are
 * those that appear in the parent node array but not the
 * child node array.
 *
 * @param array $input 
 * @return array $r - assoc arr. with root nodes as keys
 */
function get_root ($input) {
  $p = [];
  $c = [];
  $r = [];
  foreach ($input as $k => $v) {
    $p[ $v[0] ] = 1; # parent node
    $c[ $v[1] ] = 1; # child node
  }
  # find the array items in $p that aren't in $c
  foreach ($p as $k => $v) {
    if (! isset($c[$k]) ) {
      $r[$k] = 1;
    }
  }
  return $r;
}
#输入数组
$array=array(
0=>数组(
0 => 1,
1 => 3
),
1=>数组(
0 => 3,
1 => 5
),
2=>数组(
0 => 3,
1 => 7
),
3=>数组(
0 => 3,
1 => 9
),
4=>数组(
0 => 1,
1 => 10
),
5=>数组(
0 => 10,
1 => 15
));
#节点ID和节点内容的关联数组
#显然,对于菜单,节点内容可能是链接
$content=array(
1=>“猿”,
3=>“熊”,
5=>“奶牛”,
7=>“狗”,
9=>“大象”,
10=>“青蛙”,
15=>“长颈鹿”
);
$tree=[];
#简化父子关系
foreach(数组值($array)为$a){
$tree[$a[0]][$a[1]]='value';
}
$roots=get_root($array);
#开始我们的初始列表。。。
$str='
    '; foreach(数组_键($roots)为$r){ $str.=构建树($tree,$content,$r); } $str.=“
”; echo$str; /** *构建树($tree,$content,$n) * *使用数据结构$tree构建树的html表示 *包含父子关系,$content,每个 *树的节点,$n,树中的当前节点。自称 *当它遇到要构建的子树时递归地执行 * *@param数组$tree *@param array$content-assoc数组 *@param string$n-树中的当前节点 *@return string$html表示树 */ 函数构建树($tree,$content,$n){ $html=“
  • 节点id:$n;”$content[$n]。“
  • ”; #$n是否存在于$tree中,即它是否有子级? if(isset($tree[$n])){ #如果是,则启动一个新的嵌套列表 $html.='
    • '; #对于父节点$n的每个子节点, #运行构建树代码来创建html foreach(数组_键($tree[$n])作为$node){ $html.=构建树($tree,$content,$node); } $html.='
  • '; } 返回$html; } /** *获取根目录($input) * *输入数组格式: *0=>[0=>父节点id,1=>子节点id], *1=>0=>parent_node_id2,1=>child_node_id2],; * *获取父子关系的关联数组 *并生成两个数组,一个包含所有父节点, *一个包含子节点。根节点是 *出现在父节点数组中但不是 *子节点数组。 * *@param数组$input *@return数组$r-以根节点为键的关联数组 */ 函数get_root($input){ $p=[]; $c=[]; $r=[]; foreach($k=>v的输入){ $p[$v[0]]=1;#父节点 $c[$v[1]]=1;#子节点 } #查找$p中不在$c中的数组项 foreach($p为$k=>$v){ 如果(!isset($c[$k])){ $r[$k]=1; } } 返回$r; }
    上述代码()的结果:

    • 节点ID:1;内容:猿
      • 节点ID:3;内容:熊
        • 节点ID:5;内容:奶牛
        • 节点ID:7;内容:狗
        • 节点ID:9;内容:大象
      • 节点ID:10;内容:青蛙
        • 节点ID:15;内容:长颈鹿
    原始HTML(在HTML中运行):

    • 节点ID:1;内容:猿
      • 节点ID:3;内容:熊
        • 节点ID:5;内容:奶牛
        • 节点ID:7;内容:狗
        • 节点ID:9;内容:大象
      • 节点ID:10;内容:青蛙
        • 节点ID:15;内容:长颈鹿
    我的贡献。 数组中只有三种元素:

  • 父元素
  • 父元素和子元素
  • 是子元素的元素
  • 根据这三条规则,您可以构建一个菜单:

  • 循环所有元素并按编号存储父元素和子元素

    result: 3 parents: 1, 3 and 10.
            6 children: 3, 5, 7, 9, 10 and 15.
    
  • 现在我们需要过滤这些结果:

    2a:孤独的孩子是孩子的一个要素,而不是父母的一个要素

           result **real children**: 5, 7, 9, and 15 have no child of their own
    
    2b:通过从所有子项中减去孤独的子项来获得父/子组合

           result **parent/child**: 3 and 10 have a parent and child(ren)
    
    2c:获取总体价格调整
    <ul>
        <li>Node ID: 1; content: ape</li>
        <li>
          <ul>
            <li>Node ID: 3; content: bear</li>
            <li>
              <ul>
                <li>Node ID: 5; content: cow</li>
                <li>Node ID: 7; content: dog</li>
                <li>Node ID: 9; content: elephant</li>
              </ul>
            </li>
            <li>Node ID: 10; content: frog</li>
            <li>
              <ul>
                <li>Node ID: 15; content: giraffe</li>
              </ul>
            </li>
          </ul>
        </li>
    </ul>
    
    result: 3 parents: 1, 3 and 10.
            6 children: 3, 5, 7, 9, 10 and 15.
    
           result **real children**: 5, 7, 9, and 15 have no child of their own
    
           result **parent/child**: 3 and 10 have a parent and child(ren)
    
           result: **real parent** is 1
    
    $arr=array(array(1,3),array(3,5),array(3,7),array(3,9),array(1,10),array(10,15));
    $menu=array(1=>'menu 1',3=>'menu 3',5=>'menu 5',7=>'menu 7',9=>'menu 9',10=>'menu 10',15=>'menu 15');
    
    
      //1. loop array and store parents and children
    foreach($arr as $k=>$v){
        $P[$v[0]]=$v[0];
        $PC[$v[1]]=$v[0];
        }
      //2a: filter out the real children
    $C = array_diff_key($PC,$P);
      //2b: get the parent_child combinations 
    $PC=array_diff_key($PC,$C);
      //3: Get the real parent 
    $P=array_diff_key($P,$PC);
    
     //Sorting the arrays is only needed if the starting array is not ordered
    ksort($P);
    ksort($PC);
    ksort($C);
    
      //3: Building a menu
      // Create LONELY CHILDS
    foreach($C as $k=>$v){
        if(!isset($MC[$v])){$MC[$v]=array();}
        $MC[$v][]='<li>'.$menu[$k].'</li>';
        }
    
      // Build the PARENT-CHILD menu by adding the CHILDREN to their rightfull parents
    foreach($PC as $k=>$v){
        if(!isset($MPC[$v])){$MPC[$v]=array();}
        // $MPC[$v][]='<ul><li>'.$menu[$k].'</li><ul>'.implode('',$MC[$k]).'</ul></ul>'; //(OLD) 
    $MPC[$v][]='<ul><li>'.$menu[$k].'<ul>'.implode('',$MC[$k]).'</ul></li></ul>';  //**NEW**
    }
    
      // Create the REAL PARENT
    foreach($P as $k=>$v){
        if(!isset($MP[$v])){$MP[$v]=array();}
        $MP[$v][]='<ul><li>'.$menu[$k].implode('',$MPC[$k]).'</li></ul>';
        }
    
      //CREATE FINAL MENU
    $menu=array();
    foreach($MP as $k=>$v){
        $menu[]=implode('',$v);
        }
    //$menu='<ul>'.implode('',$menu).'</ul>'; //(OLD)
    $menu=implode('',$menu);  //**NEW**
    
    echo $menu;