【问题标题】:What is the next step for this tree according to morris inorder?根据 morris inorder,这棵树的下一步是什么?
【发布时间】:2013-10-29 12:43:46
【问题描述】:

就在我坐下来为 morris 中序遍历编写代码之前,我尝试了这个示例,但对于它在这种特殊情况下的工作方式有点困惑:

       80
    /      \
  60        100
    \       /
     70    90
    /
   65
  / 
 63
  \
   64

第 1 步:

   60
    \
     70
   /    \
  65     80
 /        \
63        100
 \         /
  64      90

据我了解,下一步的算法 70 会成为 65 的右孩子,那么 60 会发生什么?我很确定我错过了一些微不足道的东西,但很遗憾无法找到它。

public void MorrisInorder() {
    BSTNode<T> p = root, tmp;
    while (p != null)
        if (p.left == null) {
             visit(p);
             p = p.right;
        }
        else {
             tmp = p.left;
             while (tmp.right != null && // go to the rightmost node of
                    tmp.right != p)  // the left subtree or
                  tmp = tmp.right;   // to the temporary parent of p;
             if (tmp.right == null) {// if 'true' rightmost node was
                  tmp.right = p;     // reached, make it a temporary
                  p = p.left;        // parent of the current root,
             }
             else {                  // else a temporary parent has been
                  visit(p);          // found; visit node p and then cut
                  tmp.right = null;  // the right pointer of the current
                  p = p.right;       // parent, whereby it ceases to be
             }                       // a parent;
        }
}

我正在遵循 morris 中序遍历的代码。

【问题讨论】:

  • 我曾经编写过中序遍历,而不是 morris 中序遍历,不同之处在于它使用线程而不是堆栈或递归。我想问你是否了解基本的中序遍历,或者你只被莫里斯版本卡住了。
  • @Setilă 我非常了解中序遍历,并且我刚刚编写了一个带有添加、删除、搜索方法的线程树。我为 morris inorder 遵循的代码如上。

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


【解决方案1】:

为了直接回答您的问题,我认为您案例的第 1 步中的数字并不准确,因为不应删除从节点“80”到节点“60”的边。步骤1中唯一的变化只是将节点“70”的右点重定向到节点“80”(见步骤1),表示算法经过节点“80”的左子树后的返回路径。

第 1 步:

      80
    / ^    \
   60 |    100
    \ |    /
    70   90
    /
   65
  / 
 63
  \
  64

添加节点“70”到节点“80”的返回路径后,由于当前节点“60”的左点为NULL,则将当前节点设置为节点“70”。同时,节点“65”的右点将被重定向到节点“70”

第 2 步:

      80
    / ^    \
   60 |    100
    \ |    /
    70   90
    /^
   / |
   65
  / 
 63
  \
  64

更详细的morris中序遍历代码如下。

假设我们有一个像这样的节点结构:

/* A binary tree tNode has data, pointer to left child
and a pointer to right child */
struct tNode
{
   int data;
   struct tNode* left;
   struct tNode* right;
};

而遍历是:

/* Function to traverse binary tree without recursion and 
without stack */
void MorrisTraversal(struct tNode *root)
{
  struct tNode *current,*pre;

  if(root == NULL)
     return; 

  current = root;
  while(current != NULL)
  {
    /* This means there is no left sub-tree for current node,
       then just print current node, and go to the right "child" node.
       The right "child" node may be either its true child node,
       or the returning path for "60" sub-tree (like "70" to "80") */
    if(current->left == NULL)
    {
      printf(" %d ", current->data);
      current = current->right;      
    }    
    else
    {
      /* before going to the left sub-tree, we need to find a returning path
         to current node (such as when current node is "80", and we want to 
         go to "60", so we need to save the returning path from left sub-tree 
         to "80"). It is easy to imagine that we need to return to the current
         node when we arriving the right-most node of current left sub-tree.
         Therefore, we just go to the right-most node (the first condition in
         while) and set the returning path at "pre->right == NULL" block, as
         well as updating the current node. Another situation is that when we
         arrive at the left-most leaf node (if not exist, it means current->left
         is NULL, and we won't go into this block), we have already set the right
         point of left-most leaf node as the returning node (it un-satisfies the
         second condition of while loop), and then we will recover the right
         point of this leaf node in the next "else" block.
          */
      pre = current->left;
      while(pre->right != NULL && pre->right != current)
        pre = pre->right;

      /* Make current as right child of its inorder predecessor */
      if(pre->right == NULL)
      {
        pre->right = current;
        current = current->left;
      }

      /* Revert the changes made in if part to restore the original 
        tree i.e., fix the right child of predecssor */   
      else `enter code here`
      {
        pre->right = NULL;
        printf(" %d ",current->data);
        current = current->right;      
      } /* End of if condition pre->right == NULL */
    } /* End of if condition current->left == NULL*/
  } /* End of while */
}

【讨论】:

  • 我故意省略了右孩子重定向,但忘了在问题中提及这一点。我认为我正在关注的代码中有一个错误(使用代码编辑了我的问题),使用该代码跟随这棵树不会使所有节点成为某个其他节点的右子节点。
  • 我不知道“所有节点”是什么意思。该算法只重定向每个分支中最右节点的右子节点(极端情况是某些节点没有右子节点,然后它们的非右子节点会将其右子节点重定向到他们的父母直接)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-09-30
  • 1970-01-01
  • 2019-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-25
相关资源
最近更新 更多