目录
-
介绍
-
定义
- 深入到最里层叫做递
- 从最里层出来叫做归
- 在递的过程中,外层函数内的局部变量(以及方法参数)并未消失,归的时候还可以用到
-
特点
- 自己调用自己
- 每次调用,会缩减问题规模,最后缩减至无需递归
- 内层函数处理完成之后,外层函数才算调用完成
-
分类
- 将总数据分为两部分,已处理区域和待处理区域
- 根据处理方向
- 从前往后递归
- 已处理区域在前,待处理区域在后
- 从后往前递归
- 直接根据数学公式来写
- 可以缩减问题规模,更符合递归的特点
- 待处理区域在前,已处理区域在后
- 从后往前递归的代码比从前往后递归简洁,可以直接根据公式来写,但是思路更复杂,更难理解
- 从前往后递归
- 根据内部递归使用个数
- 单路递归
- 多路递归
-
-
单路递归
- 冒泡排序的递归实现,具体实现在:python数据结构5、排序_龙妞的博客-CSDN博客
- 插入排序的递归实现,具体实现同上
- 递归实现和迭代实现的区别
- 递归实现简化了迭代实现的最外层循环,本质上和迭代实现没有区别
-
多路递归
-
斐波那契数列
- 执行过程为二叉树
- 递归次数符合规律:f(n)=2*f(n+1)-1
- 时间复杂度:O(1.618^n)
-
兔子问题
- 问题描述
- 有一对小兔子,从出生后第3个月起每个月都生一对兔子。小兔子长到第3个月后每个月又生一对兔子。按此规律,假设没有兔子死亡,第一个月有一对刚出生的小兔子,问第n个月有多少对兔子?
- 问题分析
- 问题描述
- 有几路,执行过程就是几叉树
- python实现
-
def Fibonacci(n): if n <= 1: return n return Fibonacci(n - 1) + Fibonacci(n - 2)
-
问题优化
- 使用一个数组存储已经计算过的结果,进行剪枝
- python实现
-
def Fibonacci2(n, cache): if n <= 1: cache[n] = 1 return n if cache[n - 1] == -1: x = Fibonacci2(n - 1, cache) cache[n-1] = x else: x = cache[n-1] if cache[n - 2] == -1: y = Fibonacci2(n - 2, cache) cache[n-2] = y else: y = cache[n-2] return x + y
-
-
递归优化:记忆法/剪枝
- 存储每次递归的结果,后续再处理过程中,判断是否已经存在结果中,如果存在,则直接取值,实现了剪枝,否则执行递归
-
爆栈问题
- 尾递归
- 函数最后一步是调用某个函数
- 部分语言对尾递归进行了优化,在执行完一个函数之后,调用尾部函数之前,先释放当前函数所占用的内存,避免爆栈
- 最好的方式,就是把递归改成循环
- 尾递归
-
递归的时间复杂度公式
-
主定理
- 条件1:
- n,数据规模
- ,递归部分时间复杂度
- a,子问题个数
- ,子问题的运行时间,每个子问题被拆成原问题数据规模的n/b
- f(n),非递归部分时间复杂度
- 条件2:
- 则可套入公式
- 令,即x=\log_{子问题缩小倍数}{子问题个数},
- 条件1:
- 网站:hhtps://www.wolframalpha.com
-
-
汉诺塔问题
-
思路
- 递归前提条件,n>=1
- 目标,将罗盘从柱子A移动到柱子C
- 移动过程
- 将1~n-1视作一个整体进行移动,先将n-1通过C从A移动到B
- 将n从A移动到C
- 将n-1通过A从B移动到C
-
时间复杂度,T(n)=2T(n-1)+1=O(2^n)
-
python实现
-
def HannoTower(n, A, B, C): if n == 0: return # 把n-1~1从A->B HannoTower(n - 1, A, C, B) # 把n从A->C print(f"move dish {n} from {A} to {C} ", end=': ') x = A.pop(0) C.insert(0, x) print(A, B, C) # 把n-1~1从b->c HannoTower(n-1, B, A, C) def HannoTower2(n, A, B, C): if n == 0: return # 把n-1~1从A->B HannoTower2(n - 1, A, C, B) # 把n从A->C print("move dish %d from %c to %c " % (n, A, C)) # 把n-1~1从b->c HannoTower2(n-1, B, A, C) a = [1, 2, 3] b = [] c = [] n = len(a) print(a, b, c) HannoTower(n, a, b, c) HannoTower2(n, 'a', 'b', 'c')
-
-
杨辉三角
-
思路1、递归
-
算法流程
- 遍历每个元素的位置,双循环
- 递归计算每个元素的值
- 递归计算公式如下
- 递归结束条件,j=0或j=i
- 一次递归的返回结果,见公式
- 递归计算每个元素的值
- 遍历每个元素的位置,双循环
-
时间复杂度:O(2^N)
-
python实现
-
def element(x, y): if y == 0 or y == x: return 1 return element(x-1, y)+element(x-1, y-1) def YanghuiTriangle(n): # 递归法,O(2^N) for i in range(n): for j in range(i+1): # 每行[0, i] print(element(i, j), end=' ') print('')
-
-
思路2、二维数组优化递归
-
算法流程
- 事先生成一个空的二维数组tris,用来存储递归过程中已经计算过的元素,方便进行剪枝
- 遍历每个元素的位置,双循环
- 递归计算每个元素的值
- 递归计算公式如下
- 递归结束条件,j=0或者j=i或者当前值已经计算过
- 递归过程
- 保存f(i-1, j-1)、f(i-1,j)、f(i,j)的值到二维数组中
- 一次递归返回结果,f(i.j)
- 递归计算每个元素的值
-
时间复杂度
- 因为添加了剪枝操作,所以时间复杂度大大降低
-
python实现
-
def element2(x, y, tris): # 优化1,二维数组,空间代时间 if y == 0 or y == x: tris[x][y] = 1 return tris[x][y] if tris[x][y] != -1: # 当前值已经计算过 return tris[x][y] tris[x - 1][y - 1] = element(x - 1, y - 1) tris[x - 1][y] = element(x - 1, y) tris[x][y] = tris[x - 1][y] + tris[x - 1][y - 1] return tris[x][y] def YanghuiTriangle3(n): # 递归优化,二维数组,空间代时间,O(N^2) tris = [] for i in range(n): ls = [-1] * n tris.append(ls) for i in range(n): for j in range(i+1): # 每行[0, i] print(element2(i, j, tris), end=' ') print('')
-
-
思路3、迭代
-
算法流程
- 生成一个二维数组,用来存储结果
- 遍历每一行,遍历范围为[1, n)
- 先添加首元1
- 遍历每一列,计算中间值,遍历范围为[1, i)
- 利用之前的计算结果进行计算
- 最后添加尾元1
-
算法复杂度
- 时间复杂度:O(N^2)
- 空间复杂度:O(N^2)
-
python实现
-
def YanghuiTriangle2(n): # 迭代法,o(n^2),s(n^2) tris = [[1]] for i in range(1, n): tri = [1] for j in range(1, i): tri.append(tris[i-1][j-1]+tris[i-1][j]) tri.append(1) tris.append(tri) return tris
-
-
思路4、动态规划优化迭代
-
算法流程
- 创建一个一维数组,用来存储每一行的数据
- 遍历每一行,O(N)
- 每次迭代只修改每行的数据,O(i)
-
算法复杂度
- 时间复杂度:O(N^2)
- 空间复杂度:O(N)
-
python实现
-
def createRow(row, i): row.append(1) # 先添加结尾元素1 j = i - 1 # 从后往前修改数组的元素 while j >= 1: # j指针修改范围为[i-1, 1] row[j] += row[j - 1] j -= 1 def YanghuiTriangle4(n): # 迭代优化,动态规划优化方法,一维数组,o(n^2),s(n) if n == 1: print(1) return row = [1] print(row) for i in range(1, n): createRow(row, i) print(row)
-
-