我正在阅读二叉树教程 而且我在使用递归函数方面略有困难。比方说,我需要计算树中的节点数
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 ?? 谢谢
答案 0 :(得分:7)
使用递归函数,你应该递归思考!以下是我对这个函数的看法:
我开始编写函数的签名,即
int countNodes( TreeNode *root )
首先,不是递归的情况。例如,如果给定的树是NULL
,那么就没有节点,所以我返回0.
为什么我这样做?很简单,该函数应该适用于任何二叉树吗?那么,根节点的左子树实际上是二叉树!右子树也是二叉树。因此,我可以安全地假设使用相同的countNodes
函数,我可以计算这些树的节点。一旦我拥有它们,我只需添加左+右+ 1即可得到我的结果。
递归函数如何真正起作用?您可以使用笔和纸来遵循算法,但简而言之就是这样:
假设您使用此树调用该函数:
a
/ \
b c
/ \
d e
您看到root不为null,因此您调用左子树的函数:
b
以及后面的右子树
c
/ \
d e
在调用正确的子树之前,需要对左子树进行评估。
所以,你正在使用输入调用函数:
b
您看到root不为null,因此您调用左子树的函数:
NULL
返回0,右侧子树:
NULL
也返回0.您计算树的节点数,它是0 + 0 + 1 = 1.
现在,原始树的左侧子树
得1b
并且函数被调用
c
/ \
d e
在这里,您再次为左子树
调用该函数d
与b
的情况类似,返回1,然后是右子树
e
也会返回1并且您将树中的节点数评估为1 + 1 + 1 = 3.
现在,您返回该函数的第一个调用,并将树中的节点数评估为1 + 3 + 1 = 5.
正如您所看到的,对于每个左右,您再次调用该函数,如果他们有左或右子,则该函数会一次又一次地被调用,并且每次在树中更深入时。因此,root->left->left
或root->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);
,其中root
为myTree
。它会重新输入您的countNodes
函数,整个树的根目录为root->left
(myTree->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并重复相同的过程。尝试用一棵小树来形象化:
因此,在这种情况下,您的函数将转到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的基础,所以在你完全理解它之前,值得研究它。另请参阅quicksort,Fibonocci 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(左,右)。在此之后,您应该能够计算整个树的节点数。您发布的递归就是这样做的。