目录
一、递归 —— 时间复杂度
参考文献:https://leetcode-cn.com/explore/orignial/card/recursion-i/259/complexity-analysis/1222/
二、递归 —— 空间复杂度
参考文献:https://leetcode-cn.com/explore/orignial/card/recursion-i/259/complexity-analysis/1223/
三、尾递归
3.1 基本说明
# Python implementation
def sum_non_tail_recursion(ls):
"""
:type ls: List[int]
:rtype: int, the sum of the input list.
"""
if len(ls) == 0:
return 0
# not a tail recursion because it does some computation after the recursive call returned.
return ls[0] + sum_non_tail_recursion(ls[1:])
def sum_tail_recursion(ls):
"""
:type ls: List[int]
:rtype: int, the sum of the input list.
"""
def helper(ls, acc):
if len(ls) == 0:
return acc
# this is a tail recursion because the final instruction is a recursive call.
return helper(ls[1:], ls[0] + acc)
return helper(ls, 0)
3.2 其他说明1
尾递归是一种形式,只是用这种形式表达出的概念可以被某些编译器优化 (如 C、C++ 编译器)。尾递归的特殊形式决定了这种递归代码在执行过程中是可以不需要回溯的 (普通递归都是需要回溯的)。如果编译器针对尾递归形式的递归代码作了这种优化,就可能把原本需要线性复杂度栈内存空间的执行过程用常数复杂度的空间完成。
尾递归优化 (Tail Call Optimization, TCO) 主要是对栈内存空间的优化,这个优化是 O(n) 到 O(1) 的;至于时间的优化,其实是由于对空间的优化导致内存分配的工作减少所产生的,是一个常数优化,不会带来质的变化。
尾递归形式和循环 (或者说"迭代") 形式大致就是同一个逻辑的两种表达形式而已。经过尾递归优化的尾递归代码和循环的代码的执行效率基本上是相当的。这也是 函数式编程效率上没有落后 的一个很重要的原因。
对比不同实现方法的异同 —— 计算斐波那契数列第 n 项 (C++):
// 树形递归 - 最直观的递归实现
int fib_1(int n) {
if (n <= 1) {
return 1 ;
}
else {
return fib_1(n - 1) + fib_1(n - 2) ;
}
}
// 线形递归 - 一种更高效的递归实现
int fib_2(int n) {
int fib_rec(int a , int b , int n) {
if (n <= 1) {
return 1 ;
}
else {
return a + fib_rec(b , a + b , n - 1)
}
}
return fib_rec(1 , 1 , n)
}
// 迭代实现
int fib_3(int n) {
int a = 1 , b = 1 ;
for(int i = 0 ; i < n ; ++i) {
int a_ = b , b_ = a + b ;
a = a_ ;
b = b_ ;
}
return a
}
// 尾递归
int fib_4(int n) {
int fib_iter(int a , int b , int n) {
if (n == 0) {
return a;
}
else {
return fib_iter(b , a + b , n - 1) ;
// 尾递归,进入下一个函数不再需要上一个函数的环境了,得出结果以后直接返回。
}
}
return fib_iter(1 , 1 , n) ;
}
上述 4 例有一定递进关系 (提 fib_1 只为完整,其与另 3 者不是同一类复杂度,可无视之)。尾递归就是把一个依赖上一层环境 (上下文) 的递归 (如 fib_2) 转变为一个不依赖上一层环境的递归 (如 fib_4),从而得到结果后直接返回,而无需层层回溯。