递归学习 | 字节青训营笔记

139 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的的第12天!


😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀


[@5V)GZ]0U6U)M9_NOZVUD6D.gif

🍻递归

🍻什么是递归,它是如何工作的

递归指的是在函数的定义中使用函数自身的方法。

简单说程序调用自身的编程技巧叫递归。递归的思想是把一个大型复杂问题层层转化为一个与原问题规模更小的问题,问题被拆解成子问题后,递归调用继续进行,直到子问题无需进一步递归就可以解决的地步为止

使用递归需要避免出现死循环,为了确保递归正确工作,递归程序应该包含2个属性:

  1. 基本情况(bottom cases),基本情况用于保证程序调用及时返回,不在继续递归,保证了程序可终止。
  2. 递推关系(recurrentce relation),可将所有其他情况拆分到基本案例。

举个典型例子:
从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?"从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?'从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?……'"

语法格式如下:

void recursion()
{ 
statements; ... ... ... 
recursion(); /* 函数调用自身 */ ... ... ...
} 
int main() 
{ 
recursion();
}


流程图:

image.png

🍻什么时候考虑递归

  • 当问题和子问题具有递推关系.
  • 具有递归性质的数据结构,比如链表、树、图。
  • 反向性问题,比如取反。

🍻递归与循环的区别于联系

相同点:

  • 都是通过控制一个变量的边界(或者多个),来改变多个变量为了得到所需要的值,而反复而执行的;

  • 都是按照预先设计好的推断实现某一个值求取;(请注意,在这里循环要更注重过程,而递归偏结果一点)

不同点:

  • 递归通常是逆向思维居多,“递”和“归”不一定容易发现(比较难以理解);而循环从开始条件到结束条件,包括中间循环变量,都需要表达出来(比较简洁明了)。

🍻递归的经典应用

🍻数的阶乘

下面的实例使用递归函数计算一个给定的数的阶乘:

阶乘的递归公式为:

image.png

#include<iostream>
using namespace std;
 
int F(int n)
{
	if(n==0)//递归边界
		return 1;
 
	return n*F(n-1);//递归公式
}
 
int main()
{
	int n;
	cin >> n;
	cout << F(n) << endl;
 
	return 0;
}

🍻斐波那契数列

Fibonacci数的递推公式为:

F(0)=F(1)=1, 0=<n<2

F(n)=F(n-1)+F(n-2) n>=2;

#include<iostream>
using namespace std;
 
int F(int n)//函数返回一个数对应的Fibonacci数
{
	if(n==0 || n==1)//递归边界
		return 1;
	return F(n-1) + F(n-2);//递归公式
}
 
int main()
{
	
	int n;
	while(cin >> n)
		cout << F(n) << endl;
 
	return 0;
}

🍻母牛的故事

有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第n年的时候,共有多少头母牛?

image.png

递归公式为:

f(n) = f(n-1) + f(n-3)

l=[0,1,2,3]
for i in range(4,1000):
list.append(list[i-1]+list[i-3])
while True:
n=int(input())
if n==0:
break
print(list[n])

🍻特殊的质数肋骨

农民约翰母牛总是产生最好的肋骨。你能通过农民约翰和美国农业部标记在每根肋骨上的数字认出它们。农民约翰确定他卖给买方的是真正的质数肋骨,是因为从右边开始切下肋骨,每次还剩下的肋骨上的数字都组成一个质数。

例如有四根肋骨的数字分别是:7  3  3  1,那么全部肋骨上的数字  7331是质数;三根肋骨  733是质数;二根肋骨  73  是质数;当然,最后一根肋骨  7  也是质数。7331  被叫做长度  4  的特殊质数。

写一个程序对给定的肋骨的数目  N  (1< =N< =8),求出所有的特殊质数。数字1不被看作一个质数。

def Isprime(x):
    if x < 2:
        return False
    else:
        for i in range(2, int(sqrt(x) + 1)):
            if x % i == 0:
                return False

    return True


def f(a, count, n):
    if count == n:
        print(a)
    else:
        for item in (1, 3, 7, 9):
            temp = a * 10 + item
            if Isprime(temp):
                f(temp, count + 1, n)


if __name__ == '__main__':
    n = int(input())
    f(2, 1, n)
    f(3, 1, n)
    f(5, 1, n)
    f(7, 1, n)

🍻递归倒置字符数组

完成一个递归程序,倒置字符数组。并打印实现过程
递归逻辑为:
当字符长度等于1时,直接返回
否则,调换首尾两个字符,在递归地倒置字符数组的剩下部分

a,b=input().split()
a=int(a)
b=list(b)
for i in range(a//2):
    if i!=(a-i-1):
        x=b[i]
        b[i]=b[a-i-1]
        b[a-i-1]=x
        print(''.join(b))
    else:
        break
print()
print(''.join(b))

🍻总结

🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈

递归它使我们能够以一种优雅而有效的方式解决许多问题。使用递归可以使代码更简洁清晰,可读性更好。但是递归时间和空间消耗比较大,很多计算都是重复的,调用栈可能会溢出。

🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈