章节内容
- 递归算法
- 将问题分成基线条件和递归条件
递归
递归让解决方案更清晰,并没有性能上的优势。
递归是让函数调用自身。
基线条件和递归条件
由于递归函数调用自己,因此编写这样的函数处理不好条件容易产生无限循环。
编写递归条件时,必须设置条件使递归停止。正因如此,每个递归函数都有两部分:基线条件和递归条件。
- 递归条件:函数调用自己
- 基线条件:函数不再调用自己,从而避免形成无限循环。
栈
栈是一种简单的数据结构,符合先进的元素后出(FILO)。
栈只有两个操作:
- 往栈顶压入一个元素
- 从栈顶弹出一个元素
递归在计算机内部使用被称为调用栈的栈。
下面是一个简单的函数:
def greet(name):
print "hello, " + name + "!"
greet2(name)
print "getting ready to say bye..."
bye()
def greet2(name):
print "how are you, " + name + "?"
def bye():
print "ok, bye!"
在上述代码中我们调用greet('maggie'),计算机将首先为该函数greet分配一块内存。
我们使用这些内存设置了name变量为maggie。
每当我们调用函数时,计算机都像这样将函数所涉及的所有变量的值存储在这个函数所拥有的内存中。
接下来打印hello, maggie!,再调用greet2("maggie").同样计算机也为此函数分配一块内存。
计算机使用一个栈表示这些内存,第二个函数的内存块位于第一个上方。
在打印how are you, maggie?后greet2函数调用结束并返回。此时栈顶的内存块被弹出。
现在栈顶的内存块是greet函数的内存块。这涉及一个重要的概念:调用另一个函数时,当前函数暂停并处于未完成状态。
在执行完greet2后回到greet函数,打印getting ready to say bye...之后执行byte函数,与执行greet2一样,在栈顶添加函数bye的内存块。
然后打印ok bye!并从这个函数返回。回到函数greet后没有别的代码执行就从函数greet返回。
这个栈用于存储多个函数的内存被称为调用栈。
示例代码
def fact(x):
if x == 1:
return x
else:
return x * fact(x-1)
递归调用栈
使用栈虽然很方便,但是也要付出代价:存储详尽的信息可能占用大量的内存。每个函数调用都要占用一定的内存,如果栈很高,就意味着计算机存储了大量的函数调用信息。
在这种情况下,有两种选择:
- 重新编写代码,转而使用循环
- 使用尾递归
小结
- 递归指的是调用自己的函数
- 每个递归函数都有两个条件:基线条件和递归条件
- 栈有两种操作:压入和弹出
- 所有函数调用都进入调用栈
- 调用栈可能很长,这将占用大量的内存