递归 - 二叉树

时间:2016-01-14 01:23:26

标签: c# algorithm recursion binary-tree

我有这段代码来查找二叉树的直径。 二叉树的直径:树的直径(有时称为宽度)是树中两个叶子之间最长路径上的节点数。

我试图理解下面的代码和递归。我正试着用这棵简单的树干跑。我理解当root为20时高度将变为1(Max(0,0)+1)然后返回Math.Max(0 + 0 + 1,Max(0,0))。我的理解是它将ldiameter设置为1,返回值为root = 10.这是正确的吗?并且在这一点上lh变为1.它如何变为1?另外,如果你可以帮助我逐步干这个简单的树,这将是非常有用的。

    10
  /    \
 20    30


public int FindDiameter_util(Node root, ref int height)
        {
            /* lh --> Height of left subtree
            rh --> Height of right subtree */
            int lh = 0, rh = 0;

            /* ldiameter  --> diameter of left subtree
               rdiameter  --> Diameter of right subtree */
            int ldiameter = 0, rdiameter = 0;
            if (root == null)
            {
                height = 0;
                return 0; /* diameter is also 0 */
            }
            /* Get the heights of left and right subtrees in lh and rh
              And store the returned values in ldiameter and ldiameter */
            ldiameter = FindDiameter_util(root.left, ref lh);
            rdiameter = FindDiameter_util(root.right, ref rh);

            /* Height of current node is max of heights of left and
               right subtrees plus 1*/
            height = Math.Max(lh, rh) + 1;

            return Math.Max(lh + rh + 1, Math.Max(ldiameter, rdiameter));
        }

3 个答案:

答案 0 :(得分:3)

递归是一种基于堆栈的方法。函数的递归调用将比颁发者更早执行。如果考虑函数组合的概念,可以更容易理解递归。让我们看看这个示例函数调用:

f(g(x))

正如您所看到的,f的参数是g(x),这意味着在g(x)执行f(g(x))之前需要先计算g(x),因此f(g(x)) }是g的依赖项。现在,假设f也是f(f(x)) ,所以你打电话给

f(x)

以类似的方式,f(f(x))f(f(x))的依赖关系,因为如果没有f(x)的结果,则无法计算f

如果您理解这个纯粹的数学概念,那么下一步就是将算法添加到f(f(x))作为上下文。在编程中,FindDiameter_util不一定只是计算,但在此过程中可能会发生某些状态更改。

下一步是理解重复递归的概念。在我们的情况下,我们事先不知道应该从FindDiameter_util内调用root == null多少次,因为它应该适用于任何树。那么,让我们稍微分析一下这个功能。

事实:

  • 叶子节点从return这一事实中识别出来,这也是结束符号(参见root == null
  • 叶节点的高度为0
  • 在非平凡的情况下,当节点不是叶子节点时,任务分为两个子任务(左子树和右子树)
  • 当计算子任务的结果时,则较大树的子树+ 1(我们添加当前节点)是最大高度

此处使用的策略称为Divide et Impera。这包括几个阶段: - 将任务划分为类似但较小的子任务,直到达到琐碎为止 - 征服结果,获得对逐渐更复杂的子任务的响应,直到你得到初始问题的答案

在我们的例子中,算法,简而言之就是从根到叶子,直到它在所有子树中达到平凡,这由class SomeClass{ constructor(){ let name="john doe" } static newName(){ //i want to get the "name" variable here } } 的结束符号确定,然后使用琐碎的答案得到下一个琐碎问题的答案。所以,你要从根到叶子分裂,然后从叶子回到根,征服。

答案 1 :(得分:1)

让我们通过你的简单树递归:

   []     <---- root
 /   \
[]    []   <---- children

最初调用函数时,root == 0为真,因此输入高度初始化为0:

   [h=0]     <---- root
   /   \
  []    []   <---- children

然后,您将根左侧和右侧子树的高度设置为0:

   [h = 0, lh = 0, rh = 0]     <---- root
         /    \
       []      []          <---- children

然后你递归左边的孩子,传递lh作为身高参数:

   [h = 0, lh = 0, rh = 0]     <---- root
         /    \
      [h=0]    []          <---- children

左子将为其自己的左右子树初始化其高度变量:

        [h = 0, lh = 0, rh = 0]     <---- root
            /          \
   [h=0, lh=0, rh=0]    []          <---- children

然后左边的孩子将尝试递归其自己的左子树(即使没有一个;它是null):

       [h = 0, lh = 0, rh = 0]      <---- root
            /          \
   [h=0, lh=0, rh=0]    []          <---- children
          /
        null

在这个空节点上,我们将其识别为这样,并返回0,然后返回到父节点,lh设置为0(同样,没有更改):

       [h = 0, lh = 0, rh = 0]      <---- root
            /          \
   [h=0, lh=0, rh=0]    []          <---- children

然后我们对正确的子树进行递归,但它也是null:

       [h = 0, lh = 0, rh = 0]      <---- root
            /          \
   [h=0, lh=0, rh=0]    []          <---- children
              \
             null

因此我们将0的高度返回给父级,将rh设置为0(再次):

       [h = 0, lh = 0, rh = 0]      <---- root
            /          \
   [h=0, lh=0, rh=0]    []          <---- children

到目前为止,非常无趣。但是现在我们知道了子树的高度,我们可以将当前树的高度计算为max(lh, rh) + 1,这为我们提供了这片叶子的1高度(只有一棵树有一棵树)高度为1,所以只有一个根的子树的高度为1)才有意义。

       [h = 0, lh = 0, rh = 0]      <---- root
            /          \
   [h=1, lh=0, rh=0]    []          <---- children

但是,此级别的h实际上是对根目录lh的引用,因此它也变为1

       [h = 0, lh = 1, rh = 0]      <---- root
            /          \
   [h=1, lh=0, rh=0]    []          <---- children

现在左子树已经完成,所以我们以相同的方式递归到右子树(详细信息省略):

         [h = 0, lh = 1, rh = 1]           <---- root
            /               \
   [h=1, lh=0, rh=0]  [h=1, lh=0, rh=0]    <---- children

现在我们已经在两个子树上递归,我们返回到根,现在知道它的左右子树的高度(都是1),所以它可以计算:

height = Math.Max(lh, rh) + 1;

height = Math.Max(1, 1) + 1 = 2

因此root的高度设置为2:

         [h = 2, lh = 1, rh = 1]           <---- root
            /               \
   [h=1, lh=0, rh=0]  [h=1, lh=0, rh=0]    <---- children

答案 2 :(得分:0)

最后一行最重要的是:

return Math.Max(lh + rh + 1, Math.Max(ldiameter, rdiameter));

当前树有3种情况:

1)左子树中最长的简单路径(对于currenet树)

2)右子树中最长的简单路径(对于currenet树)

3)最长的简单路径由3个部分组成:从最深节点到左子树中的根的路径,当前节点,从根到最右边子树中最深节点的路径。

我们可以递归地计算出3种可能的直径,然后选择它们的最大值。