我们可以使用Morris遍历进行后期订购吗?

时间:2016-04-03 10:57:40

标签: algorithm binary-tree tree-traversal

我访问了很多网站,但无法找到Morris postOrder遍历的任何算法。 我知道我们可以在preOrder和inOrder中使用Morris算法。如果有人指向postOrder Morris算法(如果存在的话),那将会有很大的帮助。

4 个答案:

答案 0 :(得分:7)

我将尝试解释如何使用Morris方法实现后序遍历。 预先要求: 在解释,下订单遍历之前,让我们修改有序遍历。

在按顺序遍历中,从根节点开始 1.如果当前节点已离开子节点然后找到它的有序前导并使root作为它的右子节点并向左移动。 [找到前任,在其左子树中找到最大元素] 2.如果当前节点没有离开孩子,则打印数据并向右移动。

恢复树:应该注意的主要事情是,在执行步骤1时,我们将达到前任右子项本身是当前节点的点,这只发生在整个左子项关闭并且我们开始打印时来自那里的数据。 [当你没有找到当前节点的左子时] 因此,对于这种情况,我们需要从该节点切断正确的孩子。

void MorriesInorder(BTnode root) {
if(root == null ) return; 
BTnode temp;
while ( root!=null){
   //case 2: when left child does not exist
      if ( root.left == null ) { 
               print( root.data );
               root = root.right;
    }else {
            //find predecessor 
             temp = root.left; 
             while ( temp.right!=null && 
                      temp.right!=root) //  to check restore case
                   temp = temp.right;

             if ( temp.right == null ) //predecessor found, converting
            { 
                      temp.right = root; 
                      root = root.left; 
            } else {
                  print root.data;
                  temp.right = null; //  cut right child off
                  root = root.right; 
             }
    }

}}

现在回到原始问题,我们如何进行Postorder遍历。 我们将使用上述概念进行微小更改以实现后序遍历。 首先让我们有一个虚拟节点,并将整个树作为虚拟节点的左子,并使右子空。 [为什么?如果我们假设没有正确的root子,那么打印左子,然后root成为postorder遍历,对吧;)] 接下来呢?我们完成了,不...... 只对新树执行顺序没有任何意义,它仍然打印顺序遍历原始树后跟虚拟节点。

首先从案例2中删除打印数据[在inorder遍历中讨论]

关键部分:现在仔细观察内部的其他块,这是需要注意的一段代码。由于这个临时扩展树是有序遍历中的遍历主题,除了在内部else子句中,在找到临时父节点之后,p.left(包括)和p(被排除)之间的节点在修改后向右扩展树以相反的顺序处理。为了在恒定时间内处理它们,向下扫描节点链并反转右引用以引用节点的父节点。然后向上扫描相同的链,访问每个节点,并将正确的引用恢复到其原始设置。

//This is Post Order :children before node( L ,R , N)
void morrisPostorderTraversal(Node *root){

// Making our tree left subtree of a dummy Node
Node *dummyRoot = new Node(0);
dummyRoot->left = root;

//Think of P as the current node 
Node *p = dummyRoot, *pred, *first, *middle, *last;
while(p!=NULL){        

    if(p->left == NULL){
        p = p->right;
    } else{
        /* p has a left child => it also has a predeccessor
           make p as right child predeccessor of p    
        */
        pred = p->left;
        while(pred->right!=NULL && pred->right != p){
            pred = pred->right;
        }

        if(pred->right == NULL){ 

            // predeccessor found for first time
            // modify the tree

            pred->right = p;    
            p = p->left;

        }else {                          

           // predeccessor found second time
           // reverse the right references in chain from pred to p
            first = p;
            middle = p->left;              
            while(middle!=p){            
                last = middle->right;
                middle->right = first;
                first = middle;
                middle = last;
            }

            // visit the nodes from pred to p
            // again reverse the right references from pred to p    
            first = p;
            middle = pred;
            while(middle!=p){

                cout<<" "<<middle->data;  
                last = middle->right;          
                middle->right = first;
                first = middle;
                middle = last;
            }

            // remove the pred to node reference to restore the tree structure
            pred->right = NULL;    
            p = p-> right;
        }
    }
}    
}

答案 1 :(得分:2)

一旦您了解其背后的概念,这似乎很简单。

基本上,我们使用有序遍历(L-N-R)打印给定二叉树的后序遍历(L-R-N)。我们需要做的就是颠倒顺序遍历的第二部分和第三部分,即以L-R-N的方式打印。

在先前在线程创建阶段创建的线程的帮助下再次访问节点时,这意味着我们已经遍历了左子树。因此,我们需要打印所有左侧的子树节点。因此,我们将打印当前节点的左子节点和右子节点的左子树节点。

在此步骤之后,我们已经打印了所有左节点,现在我们要做的就是打印右指针的相反顺序。如果我们将其视为简单的单链接列表,其中下一个指针是右子树中每个节点的右子级。

以相反的顺序打印列表,直到当前节点为止。转到保存的有序后继节点,并遵循相同的顺序。

我希望它可以使您的事情变得更清楚。

PS:链接列表反转用于反转顺序遍历的第二部分和第三部分。

答案 2 :(得分:0)

我的Java解决方案-它有很多代码注释,但是如果您想进一步解释,请在这里向我注释。

public static void postOrder(Node root) {
    // ensures all descendants of root that are right-children
    //  (arrived at only by "recurring to right") get inner-threaded
    final Node fakeNode = new Node(0);    // prefer not to give data, but construction requires it as primitive
    fakeNode.left = root;

    Node curOuter = fakeNode;
    while(curOuter != null){    // in addition to termination condition, also prevents fakeNode printing
        if(curOuter.left != null){
            final Node curOuterLeft = curOuter.left;

            // Find in-order predecessor of curOuter
            Node curOuterInOrderPred = curOuterLeft;
            while(curOuterInOrderPred.right != null && curOuterInOrderPred.right != curOuter){
                curOuterInOrderPred = curOuterInOrderPred.right;
            }

            if(curOuterInOrderPred.right == null){
                // [Outer-] Thread curOuterInOrderPred to curOuter
                curOuterInOrderPred.right = curOuter;

                // "Recur" on left
                curOuter = curOuterLeft;

            } else {    // curOuterInOrderPred already [outer-] threaded to curOuter
                        //  -> [coincidentally] therefore curOuterLeft's left subtree is done processing
                // Prep for [inner] threading (which hasn't ever been done yet here)...
                Node curInner = curOuterLeft;
                Node curInnerNext = curInner.right;
                // [Inner-] Thread curOuterLeft [immediately backwards] to curOuter [its parent]
                curOuterLeft.right = curOuter;
                // [Inner-] Thread the same [immediately backwards] for all curLeft descendants
                //  that are right-children (arrived at only by "recurring" to right)
                while(curInnerNext != curOuter){
                    // Advance [inner] Node refs down 1 level (to the right)
                    final Node backThreadPrev = curInner;
                    curInner = curInnerNext;
                    curInnerNext = curInnerNext.right;
                    // Thread immediately backwards
                    curInner.right = backThreadPrev;
                }

                // curInner, and all of its ancestors up to curOuterLeft,
                //  are now indeed back-threaded to each's parent
                // Print them in that order until curOuter
                while(curInner != curOuter){
                    /*
                        -> VISIT
                     */
                    System.out.print(curInner.data + " ");

                    // Move up one level
                    curInner = curInner.right;
                }

                // "Recur" on right
                curOuter = curOuter.right;
            }

        } else {
            // "Recur" on right
            curOuter = curOuter.right;
        }
    }
}

class Node {
  Node left;
  Node right;
  int data;

  Node(int data) {
      this.data = data;
      left = null;
      right = null;
  }
}

答案 3 :(得分:0)

一种更简单的方法是对称地进行顺序遍历的莫里斯遍历,并以相反的顺序打印节点。

if ($repositoryUserOld->find($user->getId()) {
    $form->get('id')->addError(new FormError('User exists'));
    return $this->render('AppBundle:MyApp:user.html.twig', array('form' => $form->createView()));
}