因为我们知道树是递归数据结构,我们使用recurrsion来编写树的过程,如BST的删除方法等。
复发的优点是,我们的程序变得非常小(例如,顺序遍历的代码只有4或5行)而不是非复原程序,这种程序虽然冗长但不像理解透视中的递归程序那么复杂。这就是为什么我讨厌复发,我更喜欢写非复原程序,我在二元搜索树和avl树中做到了这一点。
现在请详细说明,更喜欢非递归程序而不是反复程序是不好或好事。“
答案 0 :(得分:6)
递归是一种与其他工具一样的工具。你没有拥有来使用所有可用的工具,但你至少应该理解它。
递归使得某类问题非常容易和优雅地解决,你对它的“仇恨”充其量也是非理性的。这只是一种不同的做事方式。
下面以递归和迭代的形式显示“规范”递归函数(阶乘),在我看来,递归形式更清楚地反映了f(1) = 1, f(n) = n*f(n-1) for n>1
的数学定义。
Iterative: Recursive:
def fact(n): def fact(n):
r = n if n == 1:
while n > 1: return 1
r = r * n return n * fact(n-1)
n = n - 1
return r
几乎只有 的地方我更喜欢递归的解决方案(对于非常适合递归的解决方案)是当堆栈大小的增长可能导致问题时(上面)阶乘函数可能是其中之一,因为堆栈增长依赖于n
,但它也可能被编译器优化为迭代解决方案)。但是这个堆栈溢出很少发生,因为:
O(logN)
,并且堆栈使用不会随着数据增加而快速增长。例如,您可以处理一个存储20亿个条目的16路树,只有7个堆栈级别(16 7 = ~26亿)。答案 1 :(得分:2)
您应该阅读Tail Recursion。通常,如果编译器设法将尾递归应用于过程,那么它非常有效,如果不是,则不是这样。
同样重要的问题是编译器的最大重复深度 - 通常它受到堆栈大小的限制。这里的缺点是没有优雅的方法来处理堆栈溢出。
答案 2 :(得分:1)
递归很优雅,但容易出现堆栈溢出。尽可能使用尾端递归,使编译器有机会将其转换为迭代解决方案。
您肯定决定使用哪种工具 - 但请记住,大多数处理树状数据结构的算法通常都是递归实现的。通常的做法是,您的代码更容易阅读,而其他代码则不那么令人惊讶。
答案 3 :(得分:0)
递归是一种工具。有时使用“递归工具”使代码更容易阅读,但不一定更容易理解。
一般来说,递归解决方案往往是一个很好的候选者,其中解决特定问题的“分而治之”方法是自然的。
通常情况下,递归是一个很好的选择,你可以看一个问题并说“啊哈,如果我知道这个问题的简单变体的答案,我可以使用该解决方案来生成我想要的答案”和“最简单的问题是P,其解决方案是S“。然后,整体解决问题的代码归结为查看数据,简化数据,递归生成一个(更简单的)答案,然后从简单的答案转到整个答案。
如果我们考虑计算树的水平的问题,答案是树的高度比孩子的“最高/最深”的高度高1倍,并且叶子的高度是1。类似下面的代码。这个问题可以迭代解决,但实际上,你可以在自己的数据结构中重新实现调用堆栈。
def tree_height (tree): if tree.is_leaf(): return 1 childmax = 0; for child in tree.children(): childmax=max(childmax, tree_height(child)) return childmax+1
值得考虑的是,Tail Call Optimization可以使一些递归函数在不断的堆栈空间中运行。