【问题标题】:create a tree structure from a set of parent-child relationships从一组父子关系创建树结构
【发布时间】:2014-10-09 08:50:42
【问题描述】:

我有一个数组数组,其中包含图表中节点之间的父子关系。每个嵌套数组的格式为

array( 0 => parent_node_id, 1 => child_node_id )

所以在这个数组中:

0 => array(
   0 => 1
   1 => 3
)

两个节点分别是1和3,节点1和节点3之间存在父子关系(外层数组索引0无关)。

1 => array(
   0 => 3
   1 => 5
),

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

这里是父子关系数组(注意外层数组的数组索引(0、1、2、3等)不代表什么):

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
)

这是它编码的数据结构的图形表示:

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

0 => array
   0 => 1
   1 => array
      0 => 3
      1 => array
         0 => 5
      2 => array
         0 => 7
      3 => array
         0 => 9
   2 => array
      0 => 10
      1 => array
         0 => 15

使用该数组中的信息,我想生成一棵树,然后我可以使用它在 html 页面中构建菜单。如何仅使用我的一系列父子关系来做到这一点?

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

【问题讨论】:

  • 0 索引是父索引,1 索引是它的子索引
  • 你能解释一下从数组 1 到数组 2 的逻辑吗?例如,null 来自哪里?
  • 即使有了图片,仍然不清楚这张图片是如何从平面数组表示中绘制出来的。现在更令人困惑了:在您的文本树中有两个根,但图片上有一个根。
  • 重写并澄清了问题——现在应该可以理解了。
  • 不要重复外部索引如何无关紧要,只需删除它们即可。我认为他们增加了混乱。

标签: php arrays


【解决方案1】:

我的贡献。 数组中只有三种元素:

  1. 父元素
  2. PARENT 和 CHILD 元素
  3. 属于 CHILD 的元素

根据这三个规则,您可以构建一个菜单:

  1. 循环所有元素并按数字存储父母和孩子。

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

    2a:孤独的孩子是孩子而不是父母

    的元素
           result **real children**: 5, 7, 9, and 15 have no child of their own
    

    2b:通过从所有孩子中减去 LONLY CHILDREN 来获得 PARENT/CHILD 组合

           result **parent/child**: 3 and 10 have a parent and child(ren)
    

    2c:通过从 PARENT 中减去 PARENT/CHILD 得到 OVERALL PARENT

           result: **real parent** is 1
    
  3. 建立一个菜单,从真正的孩子开始,将他们添加到他们的合法父母中,然后将他们添加到整个父母中。

在代码中...

$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;

以上结果:

  • 菜单 1
    • 菜单 3
      • 菜单 5
      • 菜单 7
      • 菜单 9
    • 菜单 10
      • 菜单 15

EDIT 更改了两行以创建有效的 HTML

还有一个new fiddle

【讨论】:

  • @i 惊动了外星人。已更正。
  • 我不是 OP,所以谁知道所需的结构是什么,但是您的 html 现在有效。 :^)
  • @i 惊动了外星人。看到很晚是你,而不是 OP 评论。无论如何感谢您的评论。
  • 至少我们现在都有完美的 html,即使 OP 从来没有看过这些答案...... ;^)
【解决方案2】:

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;
}

以上代码的结果(test it using this online demo):

  • 节点 ID:1;内容:猿
    • 节点 ID:3;内容:熊
      • 节点 ID:5;内容:牛
      • 节点 ID:7;内容:狗
      • 节点 ID:9;内容:大象
    • 节点 ID:10;内容:青蛙
      • 节点 ID:15;内容:长颈鹿

原始 HTML(通过 HTML 整洁运行):

<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>

【讨论】:

  • 您的 html 未验证 ;-) 您在节点 9 和节点 15 之后切换了 &lt;/li&gt;&lt;/ul&gt;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-17
  • 2015-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-21
相关资源
最近更新 更多