Java 使用常量空间和O(n)运行时编写二叉搜索树的非递归遍历

Java 使用常量空间和O(n)运行时编写二叉搜索树的非递归遍历,java,binary-tree,traversal,tree-traversal,Java,Binary Tree,Traversal,Tree Traversal,这不是作业,这是面试问题。 这里的问题是算法应该是常量空间。 我对如何在没有堆栈的情况下实现这一点一无所知,我会用堆栈发布我所写的内容,但无论如何都不相关 以下是我尝试过的:我尝试进行一次预顺序遍历,到达了最左边的节点,但我被卡在那里了。我不知道如何在没有堆栈/父指针的情况下“递归”备份 任何帮助都将不胜感激 (我将其标记为Java,因为这是我可以轻松使用的,但它显然与语言无关。)它是一个二元搜索树,因此每个节点都可以通过一系列右/左决策来访问。将该序列描述为0/1,从最低有效位到最高有效位。函

这不是作业,这是面试问题。

这里的问题是算法应该是常量空间。 我对如何在没有堆栈的情况下实现这一点一无所知,我会用堆栈发布我所写的内容,但无论如何都不相关

以下是我尝试过的:我尝试进行一次预顺序遍历,到达了最左边的节点,但我被卡在那里了。我不知道如何在没有堆栈/父指针的情况下“递归”备份

任何帮助都将不胜感激


(我将其标记为Java,因为这是我可以轻松使用的,但它显然与语言无关。)

它是一个二元搜索树,因此每个节点都可以通过一系列右/左决策来访问。将该序列描述为0/1,从最低有效位到最高有效位。函数f(0)表示“通过右分支找到的节点,直到找到一个叶;f(1)表示向左一个,其余的向右;f(2)——也就是说,二进制010——表示向右一个,然后向左一个,然后向右一个,直到找到一个叶。从n=0开始迭代f(n),直到找到每个叶。效率不高(因为每次都必须从树的顶部开始)但内存和线性时间不变。

如果使用的是基于向下指针的树,并且没有父指针或其他内存,则不可能在恒定空间中遍历


如果你的二叉树是在一个数组中,而不是在一个基于指针的对象结构中,这是可能的。但是你可以直接访问任何节点。这是一种欺骗;-)

我没有完全想清楚,但我认为这是可能的,只要你愿意在这个过程中弄乱你的二叉树

每个节点都有2个指针,所以它可以用来表示一个双链表。假设您从根到根前进。Left=Current。Now Root。Left指针是无用的,所以将其指定为Current.Right并继续到Current.Left。当您到达最左边的子节点时,您将拥有一个链表,其中一些节点上挂着树。现在迭代ov呃,就是说,在你走的时候,对你遇到的每一棵树都重复这个过程

编辑:仔细考虑。以下是按顺序打印的算法:

void traverse (Node root) {
  traverse (root.left, root);
}

void traverse (Node current, Node parent) {
  while (current != null) {
    if (parent != null) {
      parent.left = current.right;
      current.right = parent;
    }

    if (current.left != null) {
      parent = current;
      current = current.left;
    } else {
      print(current);
      current = current.right;
      parent = null;
    }
  }
}

这是一个搜索树,因此您可以随时获取下一个键/条目

您需要smth(我没有测试代码,但它非常简单)


问题的标题没有提到节点中缺少“父”指针。虽然BST不一定需要它,但许多二叉树实现都有父指针。 类节点{ 节点*左; 节点*右; 节点*父节点; 数据; })

在这种情况下,在纸上成像一个树的图表,用铅笔围绕着树,从边缘的两侧上下画(向下时,你在边缘的左侧,向上时,你在边缘的右侧)。基本上,有4种状态:
  • 西南:您位于边缘的左侧,从父对象到其左子对象
  • 东北:从一个左边的孩子,回到它的父母
  • 东南:从一个父母变成一个合适的孩子
  • 西北:从一个正确的孩子,回到它的父母
  • 遍历(节点*节点)
    {
    枚举方向{SW,NE,SE,NW};
    方向=西南;
    while(节点)
    {
    //首先,如果我在路上的话,输出节点数据:
    如果(方向==SE或方向==SW){
    流外数据;
    }
    开关(方向){
    案例SW:
    如果(节点->左){
    //如果我们有一个左撇子,继续向左走
    节点=节点->左;
    }
    else if(节点->右侧){
    //我们没有左撇子,向右走
    节点=节点->右侧;
    方向=SE;
    }
    否则{
    //没有孩子,上去。
    方向=NE;
    }
    打破
    案例SE:
    如果(节点->左){
    方向=西南;
    节点=节点->左;
    }
    else if(节点->右侧){
    节点=节点->右侧;
    }
    否则{
    方向=NW;
    }
    打破
    案例NE:
    如果(节点->右侧){
    //掉头回到右节点
    节点=节点->右侧;
    方向=SE;
    }
    否则{
    节点=节点->父节点;
    }
    打破
    案例NW:
    节点=节点->父节点;
    打破
    }
    }
    }
    
    Morris Inorder树遍历如何?它基于线程树的概念,它修改树,但在完成后将其还原

    林奇:


    不使用任何额外的空间。

    我们可以遍历二叉树而无需修改树本身(前提是节点具有父指针)。这可以在常量空间中完成。我发现了这个有用的链接
    接受的答案需要进行以下更改,否则它将不会打印BST只有一个节点的树

    if (current == NULL && root != NULL)
       print(root);
    

    这是iluxa的简短版本。 它以完全相同的顺序运行完全相同的节点操作和打印步骤,但以简化的方式[1]:

    void traverse (Node n) {
      while (n) {
        Node next = n.left;
        if (next) {
          n.left = next.right;
          next.right = n;
          n = next;
        } else {
          print(n);
          n = n.right;
        }
      }
    }
    
    [1]
    此外,它甚至在树根节点没有剩余子节点的情况下也能工作。

    以上iluxa回答的一个小特例

    if(current== null)
            {
                current = root;
                parent = current.Right;
                if(parent != null)
                {
                    current.Right = parent.Left;
                    parent.Left = current;
                }
            }
    

    如果没有堆栈或父指针,我也看不到这样做的方法。Mh,每个节点都有对其父节点的引用吗?@ChrisWue-nope,这很简单:)你确定他要求的是预顺序遍历而不是任何其他遍历吗?任何遍历都可以,我只是从预顺序遍历开始,因为这就是为什么我最清楚。你需要
    log(n)
    位来完成这项工作,所以我怀疑这是否符合条件
    if (current == NULL && root != NULL)
       print(root);
    
    void traverse (Node n) {
      while (n) {
        Node next = n.left;
        if (next) {
          n.left = next.right;
          next.right = n;
          n = next;
        } else {
          print(n);
          n = n.right;
        }
      }
    }
    
    if(current== null)
            {
                current = root;
                parent = current.Right;
                if(parent != null)
                {
                    current.Right = parent.Left;
                    parent.Left = current;
                }
            }