【问题标题】:How to do in-order traversal of a BST without recursion or stack but using parent pointers?如何在没有递归或堆栈但使用父指针的情况下按顺序遍历 BST?
【发布时间】:2012-05-09 10:53:53
【问题描述】:

是否可以在不使用visited 标志或stack 的情况下对其节点具有父指针(根的父节点是null)的BST 执行迭代顺序遍历?

我用谷歌搜索并没有找到回复。关键是,我怎么知道 - 在某个节点 - 我刚刚到达它而不是我已经完成了它下面的所有内容?

【问题讨论】:

  • 递归?虽然这是对堆栈的间接使用。
  • 这听起来像是那些愚蠢的面试问题之一。递归很可能是预期的答案。
  • [@Shubham, @pablochan] 如果你再读一遍这个问题,你会发现iterative这个词写得很明确。
  • 那么答案是否定的(除非您可以将访问过的节点保存在某处)
  • @pablochan,你确定吗?我认为您可以这样做,请参阅我的回答。

标签: algorithm binary-search-tree tree-traversal iteration inorder


【解决方案1】:

你可以这样做,你只需要记住最后访问的节点和当前节点。问题陈述不允许这样做:每个节点上的visited 标志和stack 都是(最坏情况)O(n),记住最后一个节点只是O(1)。

在 C# 中,算法可能如下所示:

static void Walk(Node node)
{
    Node lastNode = null;
    while (node != null)
    {
        if (lastNode == node.Parent)
        {
            if (node.Left != null)
            {
                lastNode = node;
                node = node.Left;
                continue;
            }
            else
                lastNode = null;
        }
        if (lastNode == node.Left)
        {
            Output(node);

            if (node.Right != null)
            {
                lastNode = node;
                node = node.Right;
                continue;
            }
            else
                lastNode = null;
        }
        if (lastNode == node.Right)
        {
            lastNode = node;
            node = node.Parent;
        }
    }
}

【讨论】:

  • 此代码处于无限循环中。当它试图访问一个叶子时,从左边上来后,最后一个节点为空,当它打算从右边向下遍历时,它会进入(last == node.left)分支,并重新做同样的事情。
  • @Wilhelm 不,不会。请注意,右侧的分支位于if,而不是else if。因此,当它从左侧(lastNode == node.Left 分支)上来后,它立即查看右侧(lastNode == node.Right),然后才循环。
  • 那么你需要一个父指针,那是不是就不可能了?
  • @bneil,你也可以使用线程树。如果你想避免堆栈和父指针。
【解决方案2】:

这是另一种方法。我认为它本质上等同于 svick 的答案,但避免了额外的变量。这个版本是用 Python 实现的:

node=root
if node is not None:
  while node.left is not None:
    node=node.left
  while node is not None:
    output(node)
    if node.right is not None:
      node=node.right
      while node.left is not None:
        node=node.left
    else:
      while node.parent is not None and node.parent.right is node:
        node=node.parent
      node=node.parent

您最后访问的任何节点都决定了您需要访问的下一个节点。如果您刚刚访问过节点 X,那么您需要访问 X 右侧的最左侧节点。如果 X 没有右孩子,则下一个节点是节点 X 不是来自右侧的第一个祖先一边。

【讨论】:

  • Vaughn Cato 提供的代码确实按顺序遍历了树,但据我所知,一旦到达树的末端,它就会无限循环运行。有谁知道解决这个问题的方法吗?
  • @Nathan:我没有看到您所说的无限循环。当它遍历到最右边的节点时,最后一个循环应该将它发送回根节点,最后将节点设置为根节点的父节点,它应该是无。然后外部的while循环将终止。
  • 漂亮干净的实现 +1
  • 测试了一棵二叉树,根=2,左=1,右=3,不行,只输出左节点。
  • @Patrick:我无法重现该问题。它似乎工作正常:codepad.org/njxp5tba
【解决方案3】:

使用svick 的正确想法(参见他的answer),这是C++ 中的测试 代码。请注意,我没有测试他的代码,甚至没有看它,我只是采纳了他的想法并实现了我自己的功能。

void in_order_traversal_iterative_with_parent(node* root) {
node* current = root;
node* previous = NULL;

while (current) {
    if (previous == current->parent) { // Traversing down the tree.
        previous = current;
        if (current->left) {
            current = current->left;
        } else {
            cout << ' ' << current->data;
            if (current->right)
                current = current->right;
            else
                current = current->parent;
        }
    } else if (previous == current->left) { // Traversing up the tree from the left.
        previous = current;
        cout << ' ' << current->data;
        if (current->right)
            current = current->right;
        else
            current = current->parent;
    } else if (previous == current->right) { // Traversing up the tree from the right.
        previous = current;
        current = current->parent;
    }
}

cout << endl;
}

【讨论】:

    【解决方案4】:
    public void inorderNoStack() {
        if (root == null) {
            return;
        }
    
        // use the previous to always track the last visited node
        // helps in deciding if we are going down/up
        Node prev = null;
    
        Node curr = root;
    
        while (curr != null) {
            // going down
            if (prev == null || prev.left == curr || prev.right == curr) {
                if (curr.left != null) {
                    prev = curr;
                    curr = curr.left;
                    continue;
                } else {
    
                    visitn(curr);
    
                    if (curr.right != null) {
                        prev = curr;
                        curr = curr.right;
                        continue;
                    } else {
                        // swap states
                        prev = curr;
                        curr = prev.parent;
                    }
                }
            }
    
            // going up after left traversal
            if (curr != null && prev == curr.left) {
    
                visitn(curr);
    
                if (curr.right != null) {
                    prev = curr;
                    curr = curr.right;
                    continue;
                } else {
                    // swap states
                    prev = curr;
                    curr = prev.parent;
                }
            }
    
            // going up after right traversal
            if (curr != null && prev == curr.right) {
                // swap states
                prev = curr;
                curr = prev.parent;
            }
        }
    }
    

    【讨论】:

    • 只包含代码而没有解释的答案不是很有用。
    【解决方案5】:

    我的 Java 解决方案没有在现有树上引入任何标志。也没有父指针。这种方法将使节点保持在树的高度。请看一下。

    https://github.com/skanagavelu/Algorithams/blob/master/src/tree/InOrderTraversalIterative.java

    【讨论】:

      【解决方案6】:

      第一步:编写一个返回有序后继的函数

      第2步:从最左边的节点开始,找到有序的后继,直到没有

          public class TreeNode {
            int data;
            TreeNode left;
            TreeNode right;
            TreeNode parent;
          }
      
          public class TreeUtility {
            public void inorderNoRecursion(TreeNode root) {
              TreeNode current = leftmostNode(root);
              while(current != null) {
                System.out.println(current.data);
                current = inorderSuccessor(current);
              }
            }
      
            public TreeNode inorderSuccessor(TreeNode node) {
              if (node.right!=null) {
                return leftmostNode(node.right);
              }
      
              TreeNode p = node.parent;
              TreeNode c = node;
              while(p!=null && c != p.left) {
                c = p;
                p = p.parent;
              }
              return p;
            }
      
            private TreeNode leftmostNode(TreeNode node) {
              while (node.left != null) {
                node = node.left;
              }
              return node;
            }
          }
      

      【讨论】:

        【解决方案7】:

        关键是父指针(或改变树的能力),但您需要恒定数量的额外状态(例如,以下协程的程序计数器)。

        1. 将 v 设置为根。
        2. 当 v 有左孩子时,将 v 设置为其左孩子。
        3. 产量诉
        4. 如果 v 是根,则返回。
        5. 将 p 设置为 v 的父级。
        6. 如果 p 的右孩子是 v,则将 v 设置为 p 并转到步骤 4。
        7. 产量p.
        8. 如果 p 有一个右孩子,则将 v 设置为 p 的右孩子并转到步骤 2。
        9. 将 v 设置为 p 并转到第 4 步。

        【讨论】:

        • 您的代码未能通过太多测试用例。我已经将它翻译成 C++ 中的意大利面条代码,并针对我的测试套件运行它。
        【解决方案8】:

        这是用 C++ 编写的:

        void InOrder(Node *r)
        {
           if(r==NULL)
                 return;
        
           Node *t=r;
        
           while(t!=NULL)
               t=t->left;
        
          while(t!=r)
          {
             if(t==(t->parent->left))
             {
                cout<<t->parent->data;
                t=t->parent->right;
               if(t!=NULL)
              {
               while(t!=NULL)
                  t=t->left;
              } 
              if(t==NULL)
                  t=t->parent;
             }
             if(t==t->parent->right)
             {
                t=t->parent;
             }
          }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多