Algorithm 遍历以打印使用递归排序的两个BST。不允许使用像数组这样的额外内存

Algorithm 遍历以打印使用递归排序的两个BST。不允许使用像数组这样的额外内存,algorithm,data-structures,tree,binary-search-tree,tree-traversal,Algorithm,Data Structures,Tree,Binary Search Tree,Tree Traversal,我在一次采访中被问到这个问题。给出了两个BST(二进制搜索树)。我们需要以这样一种方式遍历这两棵树,结果就是合并的排序输出。限制是我们不能像数组那样使用额外的内存。我建议按顺序组合遍历这两棵树。这种方法是正确的,但我陷入了递归,无法编写代码 注意:我们不能将这两棵树合并为一棵 请有人给我引路。 提前感谢。最简单的方法是: 将树A转换为双链接列表(已排序) 将树B转换为双链接列表(已排序) 遍历排序列表打印最小值(简单) 将列表A转换为树A 将列表B转换为树B 您可以在线找到此步骤的算法。 我认为

我在一次采访中被问到这个问题。给出了两个BST(二进制搜索树)。我们需要以这样一种方式遍历这两棵树,结果就是合并的排序输出。限制是我们不能像数组那样使用额外的内存。我建议按顺序组合遍历这两棵树。这种方法是正确的,但我陷入了递归,无法编写代码

注意:我们不能将这两棵树合并为一棵

请有人给我引路。 提前感谢。

最简单的方法是:

  • 将树A转换为双链接列表(已排序)
  • 将树B转换为双链接列表(已排序)
  • 遍历排序列表打印最小值(简单)
  • 将列表A转换为树A
  • 将列表B转换为树B
  • 您可以在线找到此步骤的算法。
    我认为并行遍历树是不可能的。您需要额外的信息,例如访问标志,以便在访问时消除左侧子树,即使这样,您也会遇到其他问题。

    如果有人知道如何通过并行遍历实现这一点,我很乐意知道

    我假设树中没有指向父节点或下一个节点的链接,否则 这将非常简单:您只需按照这些链接迭代您的树,并编写合并算法,就像编写链表一样

    如果没有下一个链接或父链接,则无法编写简单的递归。您将需要两个“递归”堆栈。 您可以实现以下结构,它允许您分别迭代每个树

    class Iterator
    {
        stack<Node> st;
        int item(){
           return st.top().item();
        }
        void advance(){
            if (st.top().right != null)
                st.push(st.top().right);
                // Go as far left as possible
                while (st.top().left != null) st.push(st.top().left);
            else {
                int x = st.top().item();
                // we pop until we see a node with a higher value
                while(st.top().item()<=x) st.pop(); 
    
            }
        }
    };
    
    类迭代器 { 斯塔克街; int项(){ 返回st.top()项(); } 作废预付款(){ if(st.top().right!=null) st.push(st.top().右侧); //尽量向左走 while(st.top().left!=null)st.push(st.top().left); 否则{ int x=st.top().item(); //我们弹出,直到看到一个值更高的节点 while(st.top().item()) 有什么问题吗

    (注意,上面是实际运行并执行任务的Haskell代码)。
    inoorder
    对于递归来说是微不足道的。
    merge
    是一个近乎标准的功能,它将两个参数有序(非递减)列表合并,生成一个有序输出列表,保留重复项

    由于延迟求值和垃圾收集,列表实际上并没有被创建——每个树最多只保留一个生成的元素,并且在生成下一个元素时被丢弃,实际上为遍历创建迭代器(每个元素都有自己的内部状态)


    这里是解决方案(如果您的语言不支持上述,或等效的
    屈服
    机制,或Scheme的显式延续,允许在每个控制堆栈的深处的两个上下文之间切换(从而使“两个递归”并行成为可能,如上所述)):



    它们没有说明任何时间复杂性,因此我们可以对第一棵树进行递归遍历,并为第一棵树的每个节点重新遍历第二棵树,同时在第1棵树上保存
    previous
    值。因此,我们在第1棵树上有两个连续的值,并在它们之间打印第二棵树的所有值,使用新的递归遍历,重新开始对于第一棵树中的每一对新值,从第二棵树的顶部取g。

    除了这两棵树之外,还需要一个比较运算符(或函数)合并树。您需要合并树或遍历以打印两个已排序树的元素吗?@Jim:谢谢您的回答。我需要遍历以打印两个已排序树的元素。@Jim:我已更新了问题标题。希望现在更清楚。@user1225752:发布您建议但无法合作的算法方法对于不认识Haskell的我来说,deVery很有趣。但是在一个采访问题中,我不确定这是采访所期望的。@Cratylus如果允许使用Python,这基本上意味着在递归遍历函数中只使用
    yield
    而不是
    return
    ,仅此而已。可能你是对的。我不知道w脚本语言,所以也许你可以在Python职位上侥幸逃脱。@Cratylus这个问题上没有特定的语言标记,对吗?如果你的语言中没有
    yield
    ,你就必须自己做一些具体化(即,在遍历树时,将递归的隐式调用堆栈转换为指向树的显式指针堆栈,如其他答案所示)或者使用标准迭代器,如果是C++,那么你就回到我原来的注释。你自己用一行代码实现你所写的东西。OP特别要求一个没有额外空间的解决方案。树的任何递归都使用O(log n)。空间。根本没有可能的解决方案使用恒定的额外空间。线性时间运算的对数空间不是什么大问题。我已经用这个解决方案给出了我的答案。你怎么想?顺便问一下,你的解决方案是否可以接受,取决于这个BST中关于
    Leaf
    表示的问题:是叶子被表示吗通过
    节点
    和两个空的子节点指针,就可以了,你可以将它们重新用于DL链接。但是如果叶子是由一个特殊的无链接
    叶子
    结构表示的,那么你必须为叶子的双链接列表中的链接分配新的内存。@WillNess:我不确定并行t的算法是什么raversal.从我的观点来看,并行遍历两棵树是行不通的,因为你不知道你是否已经访问过一棵子树。我已经澄清了我的答案。请回答::)谢谢。这里我提出了另一个要求。对于你的问题:每次遍历都是单独完成的,一个不知道另一个
    print $ merge (inorder treeA) (inorder treeB)