我可以在没有递归和堆栈的情况下对二叉树进行中序遍历吗?

发布于 2024-08-28 00:46:18 字数 43 浏览 4 评论 0原文

谁能给我一个在不使用递归和不使用堆栈的情况下按顺序遍历二叉树的解决方案?

Can anyone give me a solution for traversing a binary tree in inorder without recursion and without using a stack?

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

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

发布评论

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

评论(7

忆沫 2024-09-04 00:46:18

第二次编辑:我认为这是正确的。除了通常的node.left_child 和node.right_child 之外,还需要node.isRoot、node.isLeftChild 和node.parent。

state = "from_parent"
current_node = root
while (!done)
  switch (state)
    case "from_parent":
      if current_node.left_child.exists
        current_node = current_node.left_child
        state = "from_parent"
      else
        state = "return_from_left_child"
    case "return_from_left_child"
      if current_node.right_child.exists
        current_node = current_node.right_child
        state = "from_parent"
      else
        state = "return_from_right_child"
    case "return_from_right_child"
      if current_node.isRoot
        done = true
      else
        if current_node.isLeftChild
         state = "return_from_left_child"
        else
         state = "return_from_right_child"
        current_node = current_node.parent

Second edit: I think this is right. Requires node.isRoot, node.isLeftChild, and node.parent, in addition to the usual node.left_child and node.right_child.

state = "from_parent"
current_node = root
while (!done)
  switch (state)
    case "from_parent":
      if current_node.left_child.exists
        current_node = current_node.left_child
        state = "from_parent"
      else
        state = "return_from_left_child"
    case "return_from_left_child"
      if current_node.right_child.exists
        current_node = current_node.right_child
        state = "from_parent"
      else
        state = "return_from_right_child"
    case "return_from_right_child"
      if current_node.isRoot
        done = true
      else
        if current_node.isLeftChild
         state = "return_from_left_child"
        else
         state = "return_from_right_child"
        current_node = current_node.parent
何其悲哀 2024-09-04 00:46:18

1.双线程树

如果你的树节点有父引用/指针,那么在遍历过程中跟踪你来自哪个节点,这样你就可以决定下一步去哪里。

在Python中:

class Node:
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right
        self.parent = None
        if self.left:
            self.left.parent = self
        if self.right:
            self.right.parent = self

    def inorder(self):
        cur = self
        pre = None
        nex = None
        while cur:
            if cur.right and pre == cur.right:
                nex = cur.parent
            elif not cur.left or pre == cur.left:
                yield cur.value  # visit!
                nex = cur.right or cur.parent
            else:
                nex = cur.left
            pre = cur
            cur = nex

root = Node(1,
            Node(2, Node(4), Node(5)),
            Node(3)
        )

print([value for value in root.inorder()])  # [4, 2, 5, 1, 3]

2.单线程树

如果你的树节点没有父引用/指针,那么你可以进行所谓的莫里斯遍历,这会暂时改变树,使right属性 -没有右子节点的节点的 -- 暂时指向其中序后继节点:

在 Python 中:

class Node:
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

    def inorder(self):
        cur = self
        while cur:
            if cur.left:
                pre = cur.left
                while pre.right:
                    if pre.right is cur:
                        # We detect our mutation. So we finished
                        # the left subtree traversal.
                        pre.right = None
                        break
                    pre = pre.right
                else:  # prev.right is None
                    # Mutate this node, so it links to curr
                    pre.right = cur
                    cur = cur.left
                    continue
            yield cur.value
            cur = cur.right

root = Node(1,
            Node(2, Node(4), Node(5)),
            Node(3)
        )

print([value for value in root.inorder()])

1. Double threaded tree

If your tree nodes have parent references/pointers, then keep track of which you node you came from during the traversal, so you can decide where to go next.

In Python:

class Node:
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right
        self.parent = None
        if self.left:
            self.left.parent = self
        if self.right:
            self.right.parent = self

    def inorder(self):
        cur = self
        pre = None
        nex = None
        while cur:
            if cur.right and pre == cur.right:
                nex = cur.parent
            elif not cur.left or pre == cur.left:
                yield cur.value  # visit!
                nex = cur.right or cur.parent
            else:
                nex = cur.left
            pre = cur
            cur = nex

root = Node(1,
            Node(2, Node(4), Node(5)),
            Node(3)
        )

print([value for value in root.inorder()])  # [4, 2, 5, 1, 3]

2. Single threaded tree

If your tree nodes do not have parent references/pointers, then you can do a so-called Morris traversal, which temporarily mutates the tree, making the right property -- of a node that has no right child -- temporarily point to its inorder successor node:

In Python:

class Node:
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

    def inorder(self):
        cur = self
        while cur:
            if cur.left:
                pre = cur.left
                while pre.right:
                    if pre.right is cur:
                        # We detect our mutation. So we finished
                        # the left subtree traversal.
                        pre.right = None
                        break
                    pre = pre.right
                else:  # prev.right is None
                    # Mutate this node, so it links to curr
                    pre.right = cur
                    cur = cur.left
                    continue
            yield cur.value
            cur = cur.right

root = Node(1,
            Node(2, Node(4), Node(5)),
            Node(3)
        )

print([value for value in root.inorder()])
厌倦 2024-09-04 00:46:18

由于遍历 二叉树 需要某种状态(在访问后继节点后返回的节点),这可能由递归隐含的堆栈提供(或由数组显式提供)。

答案是否定的,你不能。 (根据经典定义)

以迭代方式最接近二叉树遍历的可能是使用

编辑:
或者如已经显示的 线程二叉树

Since traversing a binary tree requires some kind of state (nodes to return after visiting successors) which could be provided by stack implied by recursion (or explicit by an array).

The answer is no, you can't. (according to the classic definition)

The closest thing to a binary tree traversal in an iterative way is probably using a heap

EDIT:
Or as already shown a threaded binary tree ,

荆棘i 2024-09-04 00:46:18

是的,你可以。为此,您需要一个父指针才能提升树。

Yes, you can. In order to do this, you would require a parent pointer in order to ascend the tree.

左岸枫 2024-09-04 00:46:18

从tree_first() 开始,继续使用tree_next() 直到得到NULL。
完整代码: https://github.com/virtan/tree_closest

struct node {
    int value;
    node *left;
    node *right;
    node *parent;
};

node *tree_first(node *root) {
    while(root && root->left)
        root = root->left;
    return root;
}

node *tree_next(node *p) {
    if(p->right)
        return tree_first(p->right);
    while(p->parent) {
        if(!p->parent->right || p->parent->right != p)
            return p->parent;
        else p = p->parent;
    }
    return 0;
}

Start with tree_first(), continue with tree_next() until get NULL.
Full code: https://github.com/virtan/tree_closest

struct node {
    int value;
    node *left;
    node *right;
    node *parent;
};

node *tree_first(node *root) {
    while(root && root->left)
        root = root->left;
    return root;
}

node *tree_next(node *p) {
    if(p->right)
        return tree_first(p->right);
    while(p->parent) {
        if(!p->parent->right || p->parent->right != p)
            return p->parent;
        else p = p->parent;
    }
    return 0;
}
眸中客 2024-09-04 00:46:18

组合器处理程序 Combo 中使用的例程对于二叉树来说是非递归的,并且不使用堆栈和没有“向上”指针——只有两个分支指针。

这是按照 C 语言编写的伪代码。

当前状态是 (P,E,N)。迭代从 (P,E,N) ← (NULL,Root,-) 开始,其中“N”不需要初始化。结束条件是“E == NULL”。迭代器称为“DOWN(P,E,N)”,形式为 (P,E,N) ← (E,N,-)。在叶节点上,有“FLIP(P,E,N)”,即 (P,E,N) ← (N,E,P)。根据遍历的情况,它也可以用在非叶节点上。否则,叶节点通常用 SPIN(P,E,N) 处理,即 (P,E,N) ← (-, (Head: Tail(E), Tail: P), Head(E)) 。这将返回 E 的访问计数并将其增加。在某些情况下,如果退出,您将执行 UNSPIN(P,E,N),即 (P,E,N) ← (Head(E), (Head: Tail(E), Tail: N), -),它还会返回访问计数并增加它。在这两种情况下,二叉树的模 3 都会增加。

在并发环境中,在使用 SPIN 或 UNSPIN 之前,线程必须等待节点的访问计数循环通过(无论其他线程正在使用该节点)。因此,这也必须得到妥善管理。

在“Combo”的实现中,构造函数使用子树散列。因此,没有子树是重复的——甚至叶节点也不重复。它非常适合扩展到有理无限树,这将通过循环图实现。这就是意图。因此,从理论上讲(如果您正确扩展设计)您甚至可以使用它来处理无限树 - 前提是它们是有理数的。

The routine used in the combinator cruncher Combo is non-recursive, for binary trees, and uses no stacks and no "up" pointer - just the two branch pointers.

This is pseudo-code cast in the mold of C.

The current state is (P,E,N). An iteration starts with (P,E,N) ← (NULL,Root,-), where the "N" does not need to be initialized. The ending condition is where "E == NULL". The iterator is called "DOWN(P,E,N)" and has the form (P,E,N) ← (E,N,-). On leaf nodes, you have "FLIP(P,E,N)", which is (P,E,N) ← (N,E,P). It may also be used on non-leaf nodes depending on the condition of the traversal. A leaf node is normally, otherwise, handled with SPIN(P,E,N), which is (P,E,N) ← (-, (Head: Tail(E), Tail: P), Head(E)). This returns a visit count for E and bumps it up. In some cases, if backing out, you'll do UNSPIN(P,E,N), which is (P,E,N) ← (Head(E), (Head: Tail(E), Tail: N), -), which also returns the visit count and bumps it up. In both cases, it is bumped up modulo 3 for binary trees.

In a concurrent environment, a thread has to wait for a node's visit count to cycle through, by whatever other thread is using it, before using SPIN or UNSPIN. So, that has to be managed properly, too.

In the implementation of "Combo", the constructor uses sub-tree hashing. So, no subtree is ever duplicated - not even leaf nodes. It's ideally suited for scaling up to rational infinite trees, that would be implemented by cyclic graphs. That was the intent. So, theoretically (if you scale up the design right) you could even use this to work with infinite trees - provided they're rational.

千鲤 2024-09-04 00:46:18

正如这里有人已经说过的,这是可能的,但如果没有父指针则不行。父指针基本上允许您根据需要遍历“路径”,从而打印出节点。
但是为什么递归可以在没有父指针的情况下工作呢?好吧,如果您理解递归,它会是这样的(想象一下递归堆栈):

  recursion //going into
   recursion
    recursion
     recursion 
     recursion //going back up
    recursion
   recursion
  recursion

因此,当递归结束时,您就以相反的顺序打印了二叉树的所选一侧。

As someone here already stated, it is possible, but not without the parent pointer. The parent pointer basically allows you to traverse "the path" if you want, and therefore print-out the nodes.
But why does recursion works without parent pointer? Well if you understand recursion it goes something like this(imagine the recursion stack):

  recursion //going into
   recursion
    recursion
     recursion 
     recursion //going back up
    recursion
   recursion
  recursion

So when the recursion ends you then have printed the chosen side of the binary tree in reversed order.

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