我在学校里很难理解递归。每当教授谈到它的时候,我似乎都懂了,但当我自己尝试的时候,它完全震撼了我的大脑。
我整晚都在尝试着解决《Towers of Hanoi》这款游戏,结果完全出乎我的意料。我的课本只有大约30页关于递归的内容,所以它不是很有用。有人知道哪些书籍或资源可以帮助澄清这个话题吗?
我在学校里很难理解递归。每当教授谈到它的时候,我似乎都懂了,但当我自己尝试的时候,它完全震撼了我的大脑。
我整晚都在尝试着解决《Towers of Hanoi》这款游戏,结果完全出乎我的意料。我的课本只有大约30页关于递归的内容,所以它不是很有用。有人知道哪些书籍或资源可以帮助澄清这个话题吗?
当前回答
这与其说是一个问题,不如说是一个抱怨。关于递归你有更具体的问题吗?就像乘法一样,人们不会写很多关于它的东西。
说到乘法,想想这个。
问题:
* b是什么?
答:
如果b = 1,就是a。 否则就是a+a*(b-1)
* (b - 1)是什么?请参考上面的问题来解决这个问题。
其他回答
当使用递归解时,我总是尝试:
首先建立基本情况,即。 当n = 1的阶乘 试着想出一个一般的规则 对于其他情况
还有不同类型的递归解,分而治之的方法对分形和其他方法很有用。
如果你能先解决一些简单的问题,只是为了掌握窍门,这也会有帮助。一些例子是求解阶乘和生成第n个斐波那契数。
作为参考,我强烈推荐Robert Sedgewick的算法。
希望这能有所帮助。祝你好运。
递归函数就像弹簧,每次调用都要压缩一点。在每一步中,您将一些信息(当前上下文)放在堆栈上。当到达最后一步时,释放弹簧,立即收集所有值(上下文)!
不确定这个比喻是否有效…: -)
无论如何,除了经典的例子(阶乘是最糟糕的例子,因为它效率低,很容易被平化,Fibonacci, Hanoi…),这些都有点人为(我很少,如果有的话,在实际编程案例中使用它们),看看它真正被使用的地方是有趣的。
A very common case is to walk a tree (or a graph, but trees are more common, in general). For example, a folder hierarchy: to list the files, you iterate on them. If you find a sub-directory, the function listing the files call itself with the new folder as argument. When coming back from listing this new folder (and its sub-folders!), it resumes its context, to the next file (or folder). Another concrete case is when drawing a hierarchy of GUI components: it is common to have containers, like panes, to hold components which can be panes too, or compound components, etc. The painting routine calls recursively the paint function of each component, which calls the paint function of all the components it holds, etc.
不确定我是否很清楚,但我喜欢展示现实世界中教材的使用,因为这是我过去偶然发现的东西。
要理解递归,你只需要看看洗发水瓶上的标签:
function repeat()
{
rinse();
lather();
repeat();
}
这样做的问题是没有终止条件,递归将无限重复,或者直到洗发水或热水用完为止(外部终止条件,类似于吹你的堆栈)。
实际上,使用递归是为了降低手头问题的复杂性。你应用递归,直到你达到一个简单的基本情况,可以很容易地解决。这样就可以解决最后一个递归步骤。用这些递归步骤就可以解决最初的问题。
想想工蜂。它试着酿蜂蜜。它完成了自己的工作,并期待其他工蜂来酿造剩下的蜂蜜。蜂房满了,蜂房就停了。
把它想象成魔法。你有一个与你要实现的函数同名的函数,当你给它子问题时,它就会帮你解决它,你唯一需要做的就是把你的部分的解与它给你的解集成起来。
例如,我们想计算一个列表的长度。让我们用magical_length来调用我们的函数,用magical_length来调用神奇的助手 我们知道,如果我们给出没有第一个元素的子列表,它会神奇地给我们子列表的长度。那么我们唯一需要考虑的就是如何将这些信息与我们的工作结合起来。第一个元素的长度是1,而magic_counter给出了子列表的长度n-1,因此总长度是(n-1) + 1 -> n
int magical_length( list )
sublist = rest_of_the_list( list )
sublist_length = magical_length( sublist ) // you can think this function as magical and given to you
return 1 + sublist_length
然而,这个答案是不完整的,因为我们没有考虑如果我们给出一个空列表会发生什么。我们认为我们的列表总是至少有一个元素。因此,我们需要思考,如果给我们一个空列表,答案显然是0,那么答案应该是什么。所以把这些信息加到我们的函数中,这被称为基础/边缘条件。
int magical_length( list )
if ( list is empty) then
return 0
else
sublist_length = magical_length( sublist ) // you can think this function as magical and given to you
return 1 + sublist_length