【问题标题】:Having trouble understanding tree traversal recursive functions难以理解树遍历递归函数
【发布时间】:2016-02-22 11:24:22
【问题描述】:

我在理解前序、中序和后序树遍历中涉及的递归函数时遇到了一些麻烦。我对递归有一些了解(但不可否认,这不是我的强项)。所有这些似乎都调用了自己两次,首先是用根的左孩子打电话,然后是用右孩子打电话。但这到底是怎么可能的呢?用左子调用 preOrder 函数不会将控制流返回到顶部,并且永远不会执行下一个调用吗?

void preOrder (Node* root) 
{
    if (root == NULL) return;
    cout<<root->val<<", ";
    preOrder(root->left);
    preOrder(root->right);
}

【问题讨论】:

    标签: recursion binary-tree binary-search-tree preorder


    【解决方案1】:

    预排序:处理节点,移至左孩子,移至右孩子

    void preorder(Node *root)
    {
        if (root == NULL) //<- Check if the currently passed node is empty
            return; //<- if it is, return from the function back down the stack
    
        cout << root->val << ", "; //<- if it wasn't empty, print its value
    
        if (root->left != NULL) //<- check if the node to the left is empty
            preorder(root->left); //<- if not recurse to the left child
    
        if (root->right != NULL) //<- check if the node to the right is empty
            preorder(root->right); //<- if not recurse to the right child
    }
    

    中序:向左移动,处理节点,向右移动

    void inorder(Node *root)
    {
        if (root == NULL)
            return;
    
        if (root->left != NULL) //<- check if the node to the left is empty
                inorder(root->left); //<- if not recurse to the left child
    
        cout << root->val << ", ";
    
        if (root->right != NULL) //<- check if the node to the right is empty
                inorder(root->right); //<- if not recurse to the right child
    }
    

    后序:移动到左边节点,移动到右边节点,处理节点

    void postorder(Node *root)
    {
        if (root == NULL)
            return;
    
        if (root->left != NULL) //<- check if the node to the left is empty
                postorder(root->left); //<- if not recurse to the left child
    
        if (root->right != NULL) //<- check if the node to the right is empty
                postorder(root->right); //<- if not recurse to the right child
    
        cout << root->val << ", "
    }
    

    【讨论】:

    • 1.不要将任意节点称为root。 2. 如果您测试 inside 函数的参数为​​ NULL,那么您不必在 before 递归调用 leftright 指针上测试它.当然,这可以节省您对不存在的叶子的子节点的无效调用,但是 OTOH 它增加了所有内部节点的测试开销。
    • @CiaPan 对节点进行空测试,如果源根可能为空,则简单地返回。
    • 我知道它是干什么用的。我只是指出,在postorder(node-&gt;left) 之前测试if(node-&gt;left) 是多余的,因为函数会在调用后再次测试它。
    【解决方案2】:

    用左子函数调用 preOrder 函数会不会将控制流返回到顶部,并且永远不会执行下一个调用?

    当然不是。递归调用就像任何其他函数调用一样工作:函数完成后,控件返回调用位置。 “地点”不仅意味着代码中的点,还意味着调用堆栈上的点,这意味着同一组变量再次可用。就像从任何其他函数返回后一样。

    例如,如果你有一棵树

            A
           / \
          X   Y
    

    然后你在A 节点上调用preorder,然后它首先打印A 内容,然后在X 上调用preorder,返回时它又回到preorder(A),所以它继续拨打preorderY

    【讨论】:

      【解决方案3】:
      void preorder(node *p) {
         if(p) {
           cout << p->val <<"\n";
           preorder(p->left);
           preorder(p->right);
         }
      }
      

      【讨论】:

      • 欢迎来到 Stack Overflow!这个问题是在寻找一个解释,而不仅仅是为了工作代码。您的回答没有为提问者提供任何见解,可能会被删除。请edit解释导致观察到的症状的原因。
      • 欢迎来到 Stack Overflow!虽然此代码 sn-p 可能是解决方案,但including an explanation 确实有助于提高您的帖子质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
      猜你喜欢
      • 1970-01-01
      • 2014-12-21
      • 2013-07-23
      • 2018-07-12
      • 2014-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多