递归:
1.定义:在一个函数过程中出现直接或间接调用函数本身,称为函数的递归调用。
eg:有一个学生在一起,问第五个学生有多少岁,他说比第4个学生大2岁。问第四
个人多少岁,他说比第三个人大两岁。问第三个人有多少岁,第三个人说他比第二
个人大两岁。问第二个人多少岁,第二个人说他比第一个人大两岁。第一个人10岁。
第一种解法:用循环
第二种解法:
首先我们要了解递归的两大要素:
(1)边界条件:确定递归何时终止,也称递归的出口
(2)递归模式:大问题是如何分解成小问题的,也称递归体.(缩小问题的规模)
假如我们要求第n个人的年龄,我们可以将这个问题分成n步:
(1):求第n个人的年龄
(2):求第n-1个人的年龄
(3):求第n-2个人的年龄
......
(4):求第二个人的年龄
(5)求第一个人的的年龄
2.递归的典型例题
斐波那契数列:1 1 2 3 5 8 13 21.....
我们可以通过上面的一组数列了解到斐波那契数列的规律:除了第一项和第二项为1,其他项就等于那一项的前两项之和。
由此我们可以得到:
然而斐波那契的数列是最不适合使用递归调用的。
我们还是通过这个例子分析:
(1):求第n个人的年龄
(2):求第n-1个人的年龄
(3):求第n-2个人的年龄
......
(4):求第二个人的年龄
(5)求第一个人的的年龄
假如我们要求第五个人的年龄,那我们必须访问第四个人的年龄;我们想要知道第四个人的年龄,就必须访问第三个人的年龄;
我们想要知道第三个人的年龄,就必须访问第二个人的年龄;我们想要知道第二个人的年龄,就必须访问第一个人的年龄。这就是我们入栈的时候。
然而我们只知道第一个人的年龄并不知道其他人的年龄,所以在入栈的时候必须把其他人的年龄保存起来。
栈的特点:先进后出,后进先出
当我们知道了第一个人的年龄后,我们就可以以此类推求出第五个人的年龄。这就到出栈的时候。
我们可以通过上面几幅图发现,斐波那契的时间复杂度和kong空间复杂度都是O(n)。比我们用循环都要慢,所以斐波那契是最不适合用递归的数列。
我们ji介绍了最不适合用递归的例子,那我们就讲一下最适合用递归的例子。
3.最适合递归的例子
汉诺塔:古代有一个梵塔,塔内有3个座A,B,C,开始A座上有64个盘子,盘子大小不等,大的在下面,小的们在上面。有一个老和尚想要把这64个盘子从A座移到C座,但规定每次只能移动一个盘子,且在移动过程中始终保持大的在上面,小的在下面。在移动过程中可以利用B座,要求找出最简单的移动步骤。如图所示
我们通过画图得到这样一个规律:有N个盘子,那我们就需要移动2的n-1次。我们可以先将A座上的N-1个盘子通过B移到C上,再将A座上的盘子移到C上,然后通过A将B上的盘子移动C上。
算法:
#include<stdio.h>
void Move(char x,char y)
{
printf("%c-%c\n ",x,y);
}
int Hanoi(int n,char a,char b,char c)
{
if(n==1||n==0)
{
Move(a,c);
}
else
{
Hanoi(n-1,a,c,b);
Move(a,c);
Hanoi(n-1,b,a,c);
}
}
int main()
{
Hanoi(1,'a','b','c');
Hanoi(3,'a','b','c');
return 0;
}