你应该如何处理递归?

时间:2012-03-28 00:39:28

标签: java recursion

我目前正在学校学习递归,当有很多递归调用时,我很难考虑方法。我只想问你应该如何考虑递归,因为我知道每一步跟踪方法调用都会变得太乏味。

我们简单介绍的不是跟踪每个递归调用,而是考虑通过归纳进行递归,但我遇到的问题是如何将归纳法应用于数学以外的情境。就像有一种方法递归打印出这样的数字:

public void blah(int n)
{
    for (int i = 0; i < n; i++)
      blah(i);
    System.out.print(n);
}

我无法思考打印出来的内容,我无法看到感应在这里是如何相关的(请原谅我的无知,如果它可以在任何地方使用)。

但我想我真正的问题是如何在不必追踪每一个方法调用的情况下解决递归问题?最好的做法只是看看基本案例和倒退工作吗? (但即便如此,我觉得我对发生的事情感到困惑。)

6 个答案:

答案 0 :(得分:5)

你可以通过here

找到关于Thinking Recursively的一个很好的解释

从链接

  • 为递归函数编写原型。

  • 撰写描述该功能的评论。

  • 确定基本情况(可能有多个)及其 溶液(S)。

  • 确定要解决的较小问题(或问题)。如果成功的话 更容易跟随,将解决方案保存到较小的中 局部变量的问题(例如,sum()示例中的小).ASSUME 递归调用工作

  • 使用较小问题的解决方案来解决更大的问题。 (如果这样做不正确,则较小的解决方案 问题也将被错误计算,因此,假设在 上一步将失败)。

答案 1 :(得分:4)

  

如何在不必跟踪每个方法调用的情况下处理递归?

有几种方法可以“理解”递归程序 - 一种是将递归调用视为黑盒子,另一种需要“播放”一些案例并猜测模式。

第一种方法假设已经编写了递归方法,并且它做了一些已知的事情。当你想到递归下降解析器时,这很有用;对于生成输出的程序(与消费输入相反),例如你的程序,它不是那么好。

第二种方法更适用于与您的示例类似的程序。播放值为0,1,2和3。

0 - 0
1 - 0 1
2 - 0 0 1 2
3 - 0 0 1 0 0 1 2 3

您注意到了这种模式吗? N的输出列出了N-1之前项目的输出,并在结尾处打印N。一旦您认为可以继续使用该模式,就会知道您对递归程序有所了解。

答案 2 :(得分:3)

这是我考虑递归的方式。实际上,它非常简单。

  • 为了阻止我需要做什么?这称为基本情况。
  • 如果我还没完成怎么办?

例如,采用经典的递归问题:F(n)= n!。 0!定义为1,其他任何大于0的定义为n * F(n-1)

在这个例子中,我满足了我的两个条件 - 当我达到0时我停止!我将任何值乘以第(n-1)个值。

我使用的另一种方法:如果可以递归完成,那么可以迭代完成。这就是说,如果我可以编写一个循环来执行相同的任务,那么我也可以编写一个递归方法来执行该任务。通常可以更容易地递归地思考某些问题(例如Ackermann's Function),而其他问题则是迭代地(例如,沿着链接列表走下去)。

您可能希望使用最适合您的迭代

答案 3 :(得分:0)

您的示例将打印出来

0 0 1 0 0 1 2 0 0 1 0 0 1 2 3 4

如果用blah(4)调用。

一般来说,在递归时我一定要先处理基本情况。之后,处理递归的状态,然后逻辑可以来。

在这个例子中,基本案例控件是i < n并且将首先出现在0 < 0这是不真实的并且打破for循环打印出0.然后下一次迭代将运行,这是从i = 0 to 1 < 1开始,在调用i = 0&lt;之后再次打印出0然后它完成循环并打印1。然后是2s转,哈哈。等等,直到每个号码轮到它为止。

答案 4 :(得分:0)

不确定如何告诉你想到它但我认为这可能有助于你掌握流动的样子。在你编码一段时间后,你会感觉到它,但很多人只是避免它,因为它让他们觉得很蠢。我很抱歉它不是用Java编写的,但它不是关于它打印的代码,所以只需在任何Linux机器或cygwin上运行它。

 perl -e 'sub foo {my $n =shift;my $depth=shift;print "\t"x$depth;print "blah($n)\n";for(my $i=0;$i<$n;$i++){foo($i,$depth+1)};print "\t"x$depth;print $n,"\n"};foo(shift);'  5

以arg为3调用,这就是它的样子:

    blah(3)
            blah(0)
            0
            blah(1)
                    blah(0)
                    0
            1
            blah(2)
                    blah(0)
                    0
                    blah(1)
                            blah(0)
                            0
                    1
            2
    3

我试着像其他人说要用更小的组件来形象化它。 I.E.除了递归之外还有什么功能呢。在这种情况下,函数从0计数到某个数字。现在考虑递归的作用。对于每个计数,它开始一个新的计数,直到达到它的数量。我经常发现将它分解为多个函数是有帮助的,这样它实际上做的就是封装,并且递归分离,但这使得递归不那么明显。

我认为使用现实世界中的问题也很有帮助,例如遍历目录层次结构或其他树结构。

答案 5 :(得分:-1)

所以我会在这里变钝,你会得到递归,或者你不会,这绝对没有错。它只是将程序员分开的事情之一(sad but true according to Joel)。现在解释递归就像你是五个是任务变得有点模糊的地方。想象一下,你有四(4)个苹果,每次我要求你数一下,你拿一个苹果送给我。我第一次和你说话时你会告诉我你有四个苹果并且给我一个。现在我们继续这个过程,直到你没有苹果,这将类似于其他人所谓的base caseexit statement,这可以确保你的函数终止。

现在,你已经不再是五岁了,如果我要求你证明N的所有实例都认为这会有用,你会怎么做?这是你的教授在通过归纳解决问题时所得到的。如何通过归纳解决的一个例子如下:我的桌子上有六罐山露,我正在喝其中一罐。我说“哇,山露的味道就像电彩虹一样。”我会用感应来说其他五罐和延伸所有山露的罐子都像电动彩虹一样。因此,在递归的情况下,您证明函数将使用相同的过程终止并正确。

这可能有助于解决问题的“琐碎”实例,例如blah(0) and blah(1) and blah(2)这将向您显示解决方案趋向于您预期的方向以及您可以使用归纳来说明此过程的事实将根据任何输入N终止。