【问题标题】:Traversing tree by LEVEL按 LEVEL 遍历树
【发布时间】:2021-01-03 07:33:55
【问题描述】:

我有下面的 json 树。

interface Tree {
    v: number, // value
    c: Tree[], // children
}

const tree: Tree = {
    v: 0, c: [
        {
            v: 1,
            c: [
                {
                    v: 4,
                    c: [
                        {
                            v: 9, c: [],
                        }
                    ],
                },
                {
                    v: 5,
                    c: [
                        {
                            v: 10, c: [],
                        }
                    ],
                },
            ],
        },
        {
            v: 2,
            c: [
                {
                    v: 6,
                    c: [
                        {
                            v: 11, c: [],
                        }
                    ],
                },
                {
                    v: 7,
                    c: [
                        {
                            v: 12, c: [],
                        }
                    ],
                }
            ],
        },
        {
            v: 3,
            c: [
                {
                    v: 8,
                    c: [
                        {
                            v: 13, c: [],
                        },
                        {
                            v: 14, c: [],
                        },
                    ]
                }
            ],
        },
    ]
}

如何按Level遍历树?

也就是说,如果每个节点上应用的函数只是console.log

上面的输入应该打印 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14

甚至更好,我们可以逐层打印,输出将是:

Level 0: [0]
Level 1: [1,2,3]
LeveL 2: [4,5,6,7,8]
Level 3: [9,10,11,12,13,14]

尝试失败

function walkTree(t){
        console.log(t.v);
        for(const child of t.c){
            walkTree(child)
        }
}

【问题讨论】:

  • 你有没有尝试过什么具体的东西?
  • 我试过的递归函数。但它通过分支而不是级别遍历
  • 向我们展示递归函数。这应该是正确的方法,但可能是您的实施失败了。
  • function walkTree(t){ console.log(t.v); for(const child of t.c){ walkTree(child) } }

标签: javascript algorithm data-structures tree


【解决方案1】:

您需要将每个级别的所有值存储在一个数组中,并在您通过每个级别时将该数组向下传递。然后在最后返回该数组,以便它返回。这就是您获得所需结构的方式。

然后循环遍历结果并使用值记录文本。

请参阅下面的示例。

const tree = {
    v: 0, 
    c: [
        {
            v: 1,
            c: [
                {
                    v: 4,
                    c: [
                        {
                            v: 9, c: [],
                        }
                    ],
                },
                {
                    v: 5,
                    c: [
                        {
                            v: 10, c: [],
                        }
                    ],
                },
            ],
        },
        {
            v: 2,
            c: [
                {
                    v: 6,
                    c: [
                        {
                            v: 11, c: [],
                        }
                    ],
                },
                {
                    v: 7,
                    c: [
                        {
                            v: 12, c: [],
                        }
                    ],
                }
            ],
        },
        {
            v: 3,
            c: [
                {
                    v: 8,
                    c: [
                        {
                            v: 13, c: [],
                        },
                        {
                            v: 14, c: [],
                        },
                    ]
                }
            ],
        },
    ]
}

function walkTree(tree, level = 0, collection = []) {

  // Select the values from this level.
  const { c, v } = tree;
  
  // Create an array for this level if it doesn't have one.
  if (!Array.isArray(collection[level])) {
    collection[level] = [];
  }
  
  // Store the value of this level in the array.
  collection[level].push(v);
  
  // Loop over sub levels and pass the collection down while incrementing the level.
  for (const subTree of c) {
    collection = walkTree(subTree, level + 1, collection);
  }
  
  // Return collection at each level.
  return collection;
}

// Start walking.
const result = walkTree(tree);

result.forEach((value, index) => {
  console.log(`Level ${index}:`, value);
});

【讨论】:

    【解决方案2】:

    这是一个使用object-scan的迭代解决方案

    一个优点是您可以访问filterFn 中的元信息,因此可以轻松进行其他处理。然而,引入依赖显然需要权衡取舍。

    // const objectScan = require('object-scan');
    
    const myTree = { v: 0, c: [{ v: 1, c: [{ v: 4, c: [{ v: 9, c: [] }] }, { v: 5, c: [{ v: 10, c: [] }] }] }, { v: 2, c: [{ v: 6, c: [{ v: 11, c: [] }] }, { v: 7, c: [{ v: 12, c: [] }] }] }, { v: 3, c: [{ v: 8, c: [{ v: 13, c: [] }, { v: 14, c: [] }] }] }] };
    
    const byLevel = (tree) => objectScan(['**'], {
      filterFn: ({ isLeaf, key, value, context }) => {
        if (isLeaf) {
          const level = (key.length - 1) / 2;
          if (!(level in context)) {
            context[level] = [];
          }
          context[level].push(value);
        }
      }
    })(tree, []);
    
    console.log(byLevel(myTree));
    /* =>
    [ [ 0 ],
      [ 3, 2, 1 ],
      [ 8, 7, 6, 5, 4 ],
      [ 14, 13, 12, 11, 10, 9 ] ]
    */
    .as-console-wrapper {max-height: 100% !important; top: 0}
    <script src="https://bundle.run/object-scan@13.7.1"></script>

    免责声明:我是object-scan的作者

    【讨论】:

      【解决方案3】:

      因为这被重新提出......

      对树进行相当简单的广度优先扫描可能如下所示:

      const breadthFirst = (tree, xs = [].concat(tree)) => 
        xs.length == 0 
          ? [] 
          : [xs .map (x => x.v), ... breadthFirst (xs. flatMap (x => x.c))]
        
      const tree = {v: 0, c: [{v: 1, c: [{v: 4, c: [{v: 9, c: []}]}, {v: 5, c: [{v: 10, c: []}]}]}, {v: 2, c: [{v: 6, c: [{v: 11, c: []}]}, {v: 7, c: [{v: 12, c: []}]}]}, {v: 3, c: [{v: 8, c: [{v: 13, c: []}, {v: 14, c: []}]}]}]}
      
      console .log (breadthFirst (tree))

      这个递归函数接受一个节点数组(如果你只提供一个将一个数组包裹在它周围),然后如果该数组为空,则返回一个空数组,否则返回一个包含 v 数组的数组每个节点的属性,后跟使用每个节点的子数组进行递归调用的所有结果。

      这给了我们这样的结果

      [[0], [1, 2, 3], [4, 5, 6, 7, 8], [9, 10, 11, 12, 13, 14]]
      

      如果你愿意的话

      [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
      

      你可以通过这个小改动来做到这一点:

      const breadthFirst = (tree, xs = [].concat(tree)) => 
        xs.length == 0 
          ? [] 
          : [...xs .map (x => x.v), ... breadthFirst (xs. flatMap (x => x.c))]
      

      最后,some arguments 反对使用默认参数。如果您希望您的函数尽可能广泛地有用——例如,您正在发布一个公共库——那么通过添加一个包装函数来删除这些默认参数可能是值得的:

      const levels = (xs) => 
        xs.length == 0 
          ? [] 
          : [xs .map (x => x.v), ... levels (xs. flatMap (x => x.c))]
      
      const breadthFirst = (tree) =>
        levels ([tree])
      

      【讨论】:

        猜你喜欢
        • 2021-10-19
        • 1970-01-01
        • 2015-07-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多