我不完全理解这些递归调用何时返回

时间:2015-09-05 22:28:29

标签: recursion

我理解简单的递归代码,但有时候它会变得有点复杂。我迷路了,不能真正遵循代码。 例如以下代码(匿名写的):

void reversetree(struct node* head)
{
//first check for the exception whether does it even exit or not
if(head==NULL)
return ;

reversetree(head->left);    //reverse call for left child
reversetree(head->right);   //same reverse call for right child 

//now next these steps will swap the children
struct node* temp=head->left;
head->left=head->right;
head->right=head->left;
//now exit the function and you are done :)
}

        6
      /   \
     3     4
    / \   / \
   1  2   8  9

让我们说如果二叉树看起来像这样,有人可以为我做一步一步的逻辑吗?根据我的理解,它首先检查根是否存在,如果是,那么它再次调用左子的函数,直到没有剩下的孩子?所以当交换它的代码被调用时?什么时候开始用合适的孩子调用函数?抱歉,我对递归并不是太明亮。

2 个答案:

答案 0 :(得分:0)

如果root不存在,则该函数退出,因为它没有子节点。

是的,处理器将跟随左子项链直到它用完,然后是最后一个节点的右子项的左子项...

对于显示的树,以下是将要发生的事情:

Reverse node 6:
  Reverse node 3:
    Reverse node 1:
      Reverse NULL
      Reverse NULL
      Swap 1's links to NULL and NULL
    Reverse node 2:
      Reverse NULL
      Reverse NULL
      Swap 2's links to NULL and NULL
    Swap 3's links to 1 and 2 
  Reverse node 4:
    Reverse node 8:
      Reverse NULL
      Reverse NULL
      Swap 8's links to NULL and NULL
    Reverse node 9:
      Reverse NULL
      Reverse NULL
      Swap 9's links to NULL and NULL
    Swap 4's links to 8 and 9
  Swap 6's links to 3 and 4

在每次调用之前测试NULL 会运行得更快。

交换步骤可以在递归调用之前进行,而不会影响速度或结果。

答案 1 :(得分:0)

通常在处理递归时,人们会倾向于用笔和纸来表达。这是一个很好的练习,但我觉得这样做更容易:

第一种看待它的方式

使用递归函数和执行相同作业的其他函数之间存在无差异。他们都做好自己的工作并返回,以便您的代码在下一行恢复。

想象一下,你有一个可以反转整棵树的工作函数reversetree_external,这样你就可以用这种方式编写你的函数没有递归

void reversetree(struct node* head)
{
    if(head==NULL)
        return ;

    // switch left and right
    struct node* temp=head->left;
    head->left=head->right;
    head->right=temp;  // There was an error here

    // Reverse subtrees
    reversetree_external(head->left);
    reversetree_external(head->right); 
}

所以你需要说服自己的一切就是给出一个像:

这样的论点
        6
      /   \
     3     4
    / \   / \
   1  2   8  9

reversetree_external撤销两个子树之前变成以下

        6
      /   \
     4     3
    / \   / \
   8   9  1  2 

如果您只传递rightleft中的一个,则在使用reversetree_external之前不会执行任何操作之前会转到该级别。

如果你传递一个叶节点,例如。 8结果是相同的8(交换空指针),而reversetree_external不会做任何事情,因为它会两次都被NULL

事实上,您可以看到reversetree可以与reversetree_external做同样的事情,因此您可以只使用reversetree而不是依赖它。

第二种看待它的方式

现在,以两种方式看待这一点很好,所以反过来,例如。以NULL开头..例如8的左节点,然后是叶子,然后是使用叶子的树,然后是使用我们事先计算的子树的完整树。

input              result
NULL               NULL

8                  8

9                  9 

1                  1

2                  2 

  4                4
 / \              / \
8   9            9   8

  3                3
 / \              / \
1   2            2   1

     6             6
   /   \         /   \
  3     4       4     3
 / \   / \     / \   / \
1  2   8  9   9  8  2   1

因此,在我的脑海中进行递归代替我在结果中使用前一个结果,你知道相同的函数会做。例如。我按照过程使用切换34,两次递归与之前完成的子树转换相同。

第一种方法是如何读取递归代码,第二种方式可能是最简单的方法,以及如何轻松地创建自己的递归函数。您从基本案例开始并测试它们,然后添加一个默认案例,如何将一个稍微复杂的问题变成一个较小的类似问题,最终成为一个基本案例。