递归递推区别分析与例题总结

递归与递推

特点

递归(recursive)

运行过程中自我调用,求解过程分为回溯和递推两个过程,占用内存多(栈数先积累后收缩,有可能爆栈),代码简洁但低效。

尾递归和递归区别⬇

function story() {
从前有座山,山上有座庙,庙里有个老和尚,一天老和尚对小和尚讲故事:story() // 尾递归,进入下一个函数不再需要上一个函数的环境了,得出结果以后直接返回。
}

function story() {
从前有座山,山上有座庙,庙里有个老和尚,一天老和尚对小和尚讲故事:story(),小和尚听了,找了块豆腐撞死了 // 非尾递归,下一个函数结束以后此函数还有后续,所以必须保存本身的环境以供处理返回值。
}

尾递归省内存、高效(相当于迭代(或者说递推?))

Python无法在语言中实现尾递归优化(Tail Call Optimization, TCO),所以采用了for, while等特殊结构代替recursive的表述(优化是编译器干的,发现尾递归结构就给优化了)。

递推(iterative)

效率比递归高,尽量递推,除非只能递归。

例题

递推例子

一般都是数学题。。重点是找递推公式(也太好偷懒了吧)

平面分割问题

直线分割平面(基本结论)

如果当前有 n 条直线,新增加一条直线(第 n+1 条直线),可以多出来 n 个交点(新的直线和之前所有的直线都有交点),而多出来 n 个交点对应到可以多出 n+1 个平面(比如从两条线,又新增一条线时,新的线和两条线都相交,作用在三个区域上,对这三个区域切分,增加三个平面)。也即:
S n + 1 = S n + ( n + 1 ) = 1 + ( n + 1 ) ( n + 2 ) 2 S_{n+1}=S_{n}+(n+1)=1+\frac{(n+1)(n+2)}{2}

扫描二维码关注公众号,回复: 11562302 查看本文章
线圈分割平面

设有n条封闭曲线画在平面上,而任何两条封闭曲线恰好相交于两点,且任何三条封闭曲线不相交于同一点,问这些封闭曲线把平面分割成的区域个数。
在这里插入图片描述

当平面上已有n-1条曲线将平面分割成an-1个区域后,第n-1条曲线每与曲线相交一次,就会增加一个区域,因为平面上已有了n-1条封闭曲线,且第n条曲线与已有的每一条闭曲线恰好相交于两点,且不会与任两条曲线交于同一点,故平面上一共增加2(n-1)个区域,加上已有的an-1个区域,一共有an-1+2(n-1)个区域。所以本题的递推关系是an=an-1+2(n-1),边界条件a1=2。

平面分割变体很多,在短时间内建立递推关系是难点。

折线分割平面

题目见链接⬆
对于折线:设第n-1条折线,把空间划分的区域为f(n-1),为了增加的区域最多,新增的折线要和之前的n-1条折线的2(n-1)条边都相交(因为是折线所以每条边交了两次),交点数为4(n-1),新增的线段数为4(n-1),射线数为2。但要注意的是,折线本身相邻的两线段只能增加一个区域。新增区域数目为4(n-1)

新增区域图示⬇
(多一个线段或射线都产生一个新区域,除了最左边那两条合起来只产生一个新区域。)
在这里插入图片描述
所以有递推公式:

f(n)=f(n-1)+4(n-1) + 1=2n2-n+1

偷懒方法

这类问题一般都有固定的公式,二维的一般是an2+bn+c,三维的一般是an3+bn2+cn+d。

用待定系数法求出各个系数就OK了。

Catalan数

C n = 1 n + 1 ( 2 n n ) = ( 2 n ) ! ( n + 1 ) ! n ! C_{n}=\frac{1}{n+1}\left(\begin{array}{c} 2 n \\ n \end{array}\right)=\frac{(2 n) !}{(n+1) ! n !}

递归例子

斐波那契数列(Fibonacci)应用

得到的是递推式,但只能用递归写,或者优化为DP

兔子繁殖问题

有雌雄一对兔子,每过两个月便可繁殖雌雄各一的一对小兔子。问过n个月后共有多少对兔子?

设满x个月时共有兔子F(x)对,满x-1个月时共有兔子F(x-1)对,第x月当月新生的兔子数目为n对。
Fx=n+F(x-1)
n=F(x-2)❗ (即第x-2个月的所有兔子到第x个月都有繁殖能力了)
Fx=F(x-1)+F(x-2)
边界条件:F0=0,F1=1
由上面的递推关系可依次得到:
F2=F1+F0=1,F3=F2+F1=2,F4=F3+F2=3,F5=F4+F3=5…

繁殖问题通常与斐波那契数列有联系。类似题:母牛的故事
可得递推式:
f ( n ) = { n n < = 4 f ( n 1 ) + f ( n 3 ) n > 4 f(n)=\left\{\begin{array}{ll} n & n<=4 \\ f(n-1)+f(n-3) & n>4 \end{array}\right.

蜜蜂爬行

一只蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行,如图所示。
在这里插入图片描述
求出蜜蜂从蜂房a爬到蜂房b的可能路线数。

蜜蜂只能通过b-2或b-1点到达b点,显然有
f n = f n 1 + f n 2 ( a + 2 < = n < = b ) f_{n}=f_{n-1}+f_{n-2} \quad(a+2<=n<=b)
边界条件为fa=fa+1=1。

汉诺塔问题

法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。

这里用递归更好理解(仅此而已,递推效率更高),也能求通项公式

  1. 以Y杆为中介,将前n-1个圆盘从X杆移到Z杆(本身就是一个n-1的汉诺塔问题了!)
  2. 将第n个圆盘移到Y杆
  3. 以X杆为中介,将Z杆上的n-1个圆盘移到Y杆(本身就是一个n-1的汉诺塔问题了!)

设f(n)为将n片圆盘所在塔全部移动到另一塔最少总次数;由递归算法可知:f(1) = 1;当n>1时,f(n) = f(n-1) + 1 + f(n-1)。计算可得通项公式:f(n)=2n-1. (n>0)

有了递推公式f(n)=2*f(n-1)+1,递归就完了(数太大了,注意数据类型是ull)

关于ULL:
unsigned long long类型是目前C语言中精度最高的数据类型,可以用来表示20以内的阶乘数据,20以外的自测。
unsigned long long的精度是64位,double或者long double 虽然也占有8个字节,但是他们的实际精度只有53位。

使用 %llu 就可以打印一个unsigned long long数据了

#include<stdio.h>

unsigned long long Hanoi(int n)
{
    if (n==1) return 1;
    return 2*Hanoi(n-1)+1;
}

int main()
{
	printf("%llu",Hanoi(64));
}

倒序输出正整数

先进后出,可以用栈实现(并不会)

例:给出正整数 n=12345,以各位数的逆序形式输出,即输出54321。

递归思想:首先输出这个数的个位数,然后再输出前面数字的个位数,直到之前没数字。

数列的递归表达式:
f ( n ) = { print ( n % 10 ) , n < 10 print ( n % 10 ) ; f ( n / 10 ) , n > = 10 f(n)=\left\{\begin{array}{ll} \operatorname{print}(n \% 10), & \mathrm{n}<10 \\ \operatorname{print}(n \% 10) ; f(n / 10), & \mathrm{n}>=10 \end{array}\right.
C代码如下:

void rev(int n)
{
    printf("%d",n%10);
    if (n > 10)
   		rev(n/10);
}

python最省事的就是切片:

print(str(s)[: : -1])

猜你喜欢

转载自blog.csdn.net/qq_45268474/article/details/107907856
今日推荐