二叉树中的递归函数解释

时间:2011-10-23 00:28:23

标签: c++ c binary-tree

我正在阅读二叉树教程  而且我在使用递归函数方面略有困难。比方说,我需要计算树中的节点数

int countNodes( TreeNode *root )    
{   
       // Count the nodes in the binary tree to which  
       // root points, and return the answer.  
    if ( root == NULL )  
       return 0;  // The tree is empty.  It contains no nodes.  
    else
   {  
       int count = 1;   // Start by counting the root.  
       count += countNodes(root->left);  // Add the number of nodes   
                                        //     in the left subtree.   
       count += countNodes(root->right); // Add the number of nodes   
                                        //    in the right subtree.  
       return count;  // Return the total.  
    }  
 }   // end countNodes()

现在我怀疑是 - >如何计算说root-> left->左边?或root-> right-> left-> left ?? 谢谢

11 个答案:

答案 0 :(得分:7)

使用递归函数,你应该递归思考!以下是我对这个函数的看法:

  • 我开始编写函数的签名,即

    int countNodes( TreeNode *root ) 
    
  • 首先,不是递归的情况。例如,如果给定的树是NULL,那么就没有节点,所以我返回0.

  • 然后,我观察到我树中的节点数是左子树的节点数加上右子树的节点数加1(根节点)。因此,我基本上调用左,右节点的函数,并添加值1。
    • 请注意,我认为该功能已正常运行!

为什么我这样做?很简单,该函数应该适用于任何二叉树吗?那么,根节点的左子树实际上是二叉树!右子树也是二叉树。因此,我可以安全地假设使用相同的countNodes函数,我可以计算这些树的节点。一旦我拥有它们,我只需添加左+右+ 1即可得到我的结果。

递归函数如何真正起作用?您可以使用笔和纸来遵循算法,但简而言之就是这样:

假设您使用此树调用该函数:

  a
 / \
b   c
   / \
  d   e

您看到root不为null,因此您调用左子树的函数:

b

以及后面的右子树

   c
  / \
 d   e

在调用正确的子树之前,需要对左子树进行评估。

所以,你正在使用输入调用函数:

b

您看到root不为null,因此您调用左子树的函数:

NULL

返回0,右侧子树:

NULL

也返回0.您计算树的节点数,它是0 + 0 + 1 = 1.

现在,原始树的左侧子树

得1
b

并且函数被调用

   c
  / \
 d   e

在这里,您再次为左子树

调用该函数
d

b的情况类似,返回1,然后是右子树

e

也会返回1并且您将树中的节点数评估为1 + 1 + 1 = 3.

现在,您返回该函数的第一个调用,并将树中的节点数评估为1 + 3 + 1 = 5.

正如您所看到的,对于每个左右,您再次调用该函数,如果他们有左或右子,则该函数会一次又一次地被调用,并且每次在树中更深入时。因此,root->left->leftroot->right->left->left不会直接评估,而是在后续调用之后进行评估。

答案 1 :(得分:3)

这基本上就是递归的作用,每次 countNodes 在它到达子节点(int count = 1;时)时都会加1,并在它试图转到下一个子节点时终止叶节点(因为叶子没有子节点)每个节点以递归方式为每个左右儿童调用 countNodes ,并且计数缓慢增加并冒泡到顶部。

尝试以这种方式查看,其中为每个节点添加1,为递归停止的不存在节点添加0:

          1
        /   \
       1     1
      / \   / \
     1   0 0   0
    / \
   0   0

每个1加起来,节点的父(每个递归级别的调用函数)加上1 + left_size + right_size并返回该结果。因此,每个阶段返回的值将是:

          4
        /   \
       2     1
      / \   / \
     1   0 0   0
    / \
   0   0

我不确定是否更清楚,但我希望它能做到。

答案 2 :(得分:2)

说你打电话给countNodes(myTree);。假设myTree不为空,countNodes最终将执行count += countNodes(root->left);,其中rootmyTree。它会重新输入您的countNodes函数,整个树的根目录为root->leftmyTree->left)。然后逻辑重复;如果没有root->left,则函数返回0.否则,它最终会再次调用count += countNodes(root->left);,但这次root实际上将是myTree->left。这样它会计算myTree->left->left。稍后它会对正确的节点做同样的事情。

答案 3 :(得分:2)

这就是递归算法的美妙之处。该函数是在当前节点及其子节点上定义的。只要对左右子项的递归调用是正确的,您只需要说服自己当前的调用是正确的。完全相同的推理适用于孩子和他们的孩子,等等......这一切都只是起作用。

答案 4 :(得分:1)

它将以root-> left->(subnode-> left)等开始,直到该分支返回0,例如如果它不是实际节点(树中的叶子);

然后最深的节点将检查root-> right并重复相同的过程。尝试用一棵小树来形象化:

enter image description here

因此,在这种情况下,您的函数将转到A-> D-> B,然后右侧节点将全部返回0,您将从C节点获得最后一个+1。

答案 5 :(得分:1)

您编写的算法实现是详尽无遗的。它访问整棵树。

如果树为空,则计数为零。 如果没有,我们得到左边的节点,我们称之为L,我们在计数中加1。

由于已经证明树的子树本身就是一棵树,我们在树上以L为根再次执行相同的算法。

我们现在以具有root权限节点的树为基础进行。

现在......这确实有效。

树的子树是树,也用于空节点或单节点。 你应该看一下树的定义。

您可以使用数学归纳来证明它,并根据归纳推理来表达您的问题。 递归算法通常使用与归纳推理非常相似的结构。

答案 6 :(得分:1)

递归函数的技巧是有一个基本案例和一个归纳步骤,就像mathematical induction一样。

基本情况是递归算法知道停止的方式。在这种情况下,它是if (root == NULL) - 此节点不代表树。此行在二叉树中的每个节点上执行,即使它当时调用每个节点root。对于树的所有节点都是错误的,但是当您开始在叶节点的子节点上调用递归例程时 - 它们都是NULL - 那么它将返回{ {1}}作为节点数。

归纳步骤是通过将未解决的问题转换为(一个或多个)已解决的问题,将递归算法从一个已解决状态转移到下一个未解决状态。您的算法需要计算树中的节点数量;你需要0当前节点,然后你有两个更简单的问题 - 左边树中的节点数,右边树上的节点数。获取这两者,将它们添加到一起,为当前节点添加1,并将其作为 this 树中的节点数返回。

这个概念确实是many algorithms in computer science的基础,所以在你完全理解它之前,值得研究它。另请参阅quicksortFibonocci sequence

答案 7 :(得分:1)

认为该计划首先在最深的分支机构。然后它向后返回计数到其前一个成员。

    A
   / \
  B   C
 / \   \
D   E   F

首先程序运行直到

count += countNodes(root->left);

它暂停了到目前为止所做的事情(显然没什么特别的)并进入B.同样的情况发生在那里它进入D.在D它也是如此。但是,我们有一个特殊情况。该计划从第

行开始
if ( root == NULL )

D的不存在的左子确实是 NULL 。因此你回来0。 然后我们回到上次暂停的地方,我们继续这样做。上次我们在B,所以我们继续走过这条线

count += countNodes(root->left);

并运行下一行

count += countNodes(root->right);

这种情况一直持续到你回到A.但是在A点再次我们在搜索A的左侧假期之后暂停了。因此我们继续正确的离开。一旦我们完成了那个分支,我们就会回到A。

此时我们还没有任何未完成的业务(暂停),所以我们只返回我们在整个过程中收集的计数。

答案 8 :(得分:0)

http://www.jargon.net/jargonfile/r/recursion.html

更新: 关键是数据结构和程序都是递归的。

  • 对于数据结构,这意味着:子树也是树。
  • 代码表示:计算树:=计算子树(并添加一个)

答案 9 :(得分:0)

每个子树都有自己的根,就像实际的树有根一样。计数与每个子树的计数相同。因此,您只需继续操作,直到到达叶节点并停止本地递归,然后返回并添加节点。

答案 10 :(得分:0)

绘制整个树,然后为所有叶节点分配1(叶节点在N级)。之后,您应该能够通过求和来计算更高级别(N-1)上每个节点生成的节点数:如果节点有两个子节点,则为1 + 1;如果节点只有一个子节点,则为1。因此,对于级别N-1上的每个节点,分配值1 + sum(左,右)。在此之后,您应该能够计算整个树的节点数。您发布的递归就是这样做的。