遛树,父母先行

发布于 2024-08-08 07:20:10 字数 91 浏览 3 评论 0原文

访问链接树的所有节点(所有节点都有对父节点和所有子节点的引用,根节点将 null 作为父节点)的最佳方法是什么,以便在其任何祖先之前不会访问任何节点?非递归的布朗尼点。

What is the best way to visit all the nodes of a linked tree (all nodes have references to parent and all children, root nodes have null as parent), so that no node is visited before any of its ancestors? Brownie points for non-recursive.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

非要怀念 2024-08-15 07:20:10

伪代码:

NodesToVisit = some stack or some list
NodesToVisit.Push(RootNode)

While NodesToVisit.Length > 0
{
   CurNode = NodesToVisit.Pop()
   For each Child C in CurNode
        NodesToVisit.Push(C)
   Visit(CurNode) (i.e. do whatever needs to be done)
}

编辑:是否递归?
从技术上讲,正如 AndreyT 和其他人在这篇文章中指出的那样,这种方法是递归算法的一种形式,其中使用显式管理的堆栈来代替CPU 堆栈的位置以及在 While 循环级别进行递归的位置。也就是说,它与递归实现本身在一些微妙但重要的方面有所不同:

  • 只有“变量”被推入堆栈;堆栈上没有“堆栈帧”和关联的返回地址,唯一的“返回地址”隐含在 while 循环中,并且只有一个实例。
  • “堆栈”可以用作列表,从而可以在列表中的任何位置获取下一个“帧”,而无需以任何方式中断逻辑。

Pseudo code:

NodesToVisit = some stack or some list
NodesToVisit.Push(RootNode)

While NodesToVisit.Length > 0
{
   CurNode = NodesToVisit.Pop()
   For each Child C in CurNode
        NodesToVisit.Push(C)
   Visit(CurNode) (i.e. do whatever needs to be done)
}

Edit: Recursive or not?
To be technically correct, and as pointed out by AndreyT and others in this post, this approach is a form of a recursive algorithm, whereby an explicitly managed stack is used in lieu of the CPU stack and where the recursion takes place at the level of the While loop. This said, it differs from a recursive implementation per se in a couple of subtle yet significant ways:

  • Only the "variables" are pushed onto the stack; there is no "stack frame" and associated return address on the stack, the only "return address" is implicit to the while loop, and there is but one instance of it.
  • The "stack" could be used as a list whereby the next "frame" could be taken anywhere in the list, without braking the logic in any way.
年华零落成诗 2024-08-15 07:20:10

您正在寻找预购遍历。我认为你可以非递归地做到这一点
队列:.用伪代码来说:

创建一个空队列,然后推送根节点。

while nonempty(q)
  node = pop(q)
  visit(node)
  foreach child(node)
    push(q,child)

You're looking for a preorder traversal. I think you can do it non-recursively with
a queue:. In pseudocode:

Create an empty queue, then push the root node.

while nonempty(q)
  node = pop(q)
  visit(node)
  foreach child(node)
    push(q,child)
素染倾城色 2024-08-15 07:20:10

如果您有到所有子级和父级的链接,那么非递归算法就相当简单了。只是忘记你有一棵树。可以把它想象成一个迷宫,其中每个父子链接都是从一个路口到另一个路口的普通双向走廊。要走完整个迷宫,你需要做的就是在每个路口拐进左边的下一个走廊。 (或者,可以将其视为在迷宫中行走,左手始终接触左侧的墙壁)。如果您从根结点开始(并向任何方向移动),您将遍历整棵树,始终先访问父母,然后再访问孩子。在这种情况下,每个“走廊”将被行驶两次(在一个方向和另一个方向),并且每个“交汇处”(节点)将被访问与加入它的“走廊”一样多的次数。

If you have links to all children and to the parent as well, then non-recursive algorithm is rather trivial. Just forget that you have a tree. Think of it is a labirynth where each parent-child link is an ordinary bi-directional corridor from one junction to the other. All you need to do to walk the entire labyrinth is to turn into the next corridor on the left at each junction. (Alternatively, think of it as walking the labyrinth with your left hand always touching the wall on the left side). If you start from the root junction (and move in any direction), you'll walk the entire tree always visiting parents before children. Each "corridor" in this case will be travelled twice (in one direction and in the other), and each "junction" (node) will be visited as many times as many "corridors" join it.

蒲公英的约定 2024-08-15 07:20:10

使用一组节点。将根放入集合中以启动。然后在循环中,从集合中拉出一个节点,访问它,然后将其子节点放入集合中。当集合为空时,您就完成了。

Use a set of nodes. Put the root in the set to start. Then in a loop, pull a node out of the set, visit it, then put its children in the set. When the set is empty, you are done.

悟红尘 2024-08-15 07:20:10

在伪代码中:

currentList = list( root )
nextList = list()
while currentList.count > 0:
    foreach node in currentList:
        nextList.add(node.children)
    currentList = nextList

In pseudocode:

currentList = list( root )
nextList = list()
while currentList.count > 0:
    foreach node in currentList:
        nextList.add(node.children)
    currentList = nextList
酒几许 2024-08-15 07:20:10

如果您从根节点开始,并且仅访问已访问过的节点的父节点/子节点,则无法遍历树以便在访问其祖先节点之前访问节点。

任何类型的遍历,深度优先(基于递归/堆栈),广度优先(基于队列),深度限制,或者只是将它们从无序集合中取出,都可以。

“最佳”方法取决于树。对于一棵非常高、树枝很少的树来说,广度优先很有效。深度优先对于有很多分支的树来说效果很好。

由于节点实际上有指向其父节点的指针,因此还有一种常量内存算法,但速度要慢得多。

If you start at the root node, and only visit the parents/children of nodes you have already visited, there is no way to traverse the tree such that you visit a node before visiting its ancestors.

Any sort of traversal, depth first (recursive/stack based), breadth first (queue based), depth-limited, or just pulling them out of an unordered set, will work.

The "best" method depends on the tree. Breadth first would work well for a very tall tree with few branches. Depth first would work well for trees with many branches.

Since the nodes actually have pointers to their parents, there is also a constant-memory algorithm, but it is much slower.

辞别 2024-08-15 07:20:10

我不同意广度优先搜索,因为空间复杂度通常是该特定搜索算法的祸根。对于这种类型的使用,使用迭代加深算法可能是更好的选择,并且它涵盖了与广度优先搜索相同类型的遍历。在处理广度优先搜索的边缘方面存在细微差别,但(伪)编码应该不会太难。

参考:
http://en.wikipedia.org/wiki/Iterative_deepening

I would disagree with breadth first search as space complexity is often the bane of that specific search algorithm. Possibly using the iterative deepening algorithm is a better alternative for this type of use, and it covers the same type of traversal as breadth first search. There are minor differences in dealing with the fringe from breadth-first search, it shouldn't be too hard to (pseudo) code out though.

Reference:
http://en.wikipedia.org/wiki/Iterative_deepening

将军与妓 2024-08-15 07:20:10

这是一种真正的非递归方法:没有堆栈,空间恒定。这段Python代码假设每个节点都包含一个子节点列表,并且节点对象没有定义相等性,因此“索引”函数正在比较身份:

def walkTree(root, visit_func):
    cur  = root
    nextChildIndex = 0

    while True:
        visit_func(cur)

        while nextChildIndex >= len(cur.children) and cur is not root:
            nextChildIndex = cur.parent.children.index(cur) + 1
            cur  = cur.parent

        if nextChildIndex >= len(cur.children):
            break

        cur = cur.children[nextChildIndex]
        nextChildIndex = 0

我确信它可以稍微完善一下,变得更加简洁和更容易阅读,但这就是要点。

Here's a truly non-recursive approach: no stack, constant space. This Python code assumes that each node contains a list of children, and that the node objects do not define equality, so that the 'index' function is comparing identities:

def walkTree(root, visit_func):
    cur  = root
    nextChildIndex = 0

    while True:
        visit_func(cur)

        while nextChildIndex >= len(cur.children) and cur is not root:
            nextChildIndex = cur.parent.children.index(cur) + 1
            cur  = cur.parent

        if nextChildIndex >= len(cur.children):
            break

        cur = cur.children[nextChildIndex]
        nextChildIndex = 0

I'm sure it could be polished up a bit, made more concise and easier to read, but that's the gist.

如果没有 2024-08-15 07:20:10

在根节点(级别 0)建立一个节点列表,依次迭代每个节点并查找直接子节点(其父节点是我们当前正在查找的节点)(级别 1),完成级别 0 后继续迭代级别 1,依此类推,直到没有剩余的未访问节点。

Build up a list of nodes at the root (level 0), iterate over each node in turn and look for direct children (whose parent node is the node we are currently looking from) (level 1), when finished with level 0 move on to iterating level 1, and so on until you have no remaining unvisited nodes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文