函数自己调用自己的分行叫递归,会产生死循环。
递归可以实现分治这种算法,就是把一个复杂的大问题,分解成若干个相同的小分问题,直到问题全部解决。
所以递归函数需要:
1、出口
2、解决一个小问题
3、调用自己
递归函数每调用一次都会在栈内存产生一份自己的拷贝,直到达到出口,才一层释放,因此使用递归非常耗费内存,与循环相比速度非常慢,能用循环解决的问题不要使用递归。
递归优缺点:
1、耗费内存、速度慢
2、就是好理解、思路清晰。
3、可以解决非线性的执行过程。
全排列:
#include <stdio.h>
void swap(int *a,int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void quanpailie(int list[], int m, int n)
{
if(m == n-1)
{
for(int i=0; i<n; i++)
{
printf("%d",list[i]);
}
printf("\n");
return;
}
for(int i=m; i<n;i++)
{
swap(&list[i],&list[m]);
quanpailie(list,m+1,n);
swap(&list[i],&list[m]);
}
}
int main(int argc,const char* argv[])
{
int a[10] = {0,1,2,3,4,5,6,7,8,9};
quanpailie(a,0,10);
}
调用后首先自己与自己交换,表示其中一个排列并确定这个此时m位置上的数,
之后递归调用(m+1)确定之后每一位上的数
直到 m =n-1;表示一个排列的组合完全确定 ,输出返回并结束。
通过for循环与两次交换
m与从自己开始每一个数都有一次交换,之后的数因为交换过一次所以不用在与之前的数交换,使每种可能只有一次
相当于从第一次调用的for中开始分出n-m种可能的分支,从第一个调用开始,可以算是分成10个分支,往后因为m+1,分支的分支逐个减少,最后所有的可能全部确定
for(int i=n;i>=0;i--)
{
int k=a[i];a[i]=a[n];a[n]=k;
quanpailie(a,n+1,m);
k=a[i];a[i]=a[n];a[n]=k;
}
这个每个m从后往前交换一个个确定,分支相当于从0开始分支逐渐增多
例如 0,1,2
首先 从0 开始 自己交换 0,1,2 交换 for后0与1交换 1,0,2 交换 for再后 2,1,0
调用函数从1开始 0,1,2 for后 0 ,2,1 1,0,2 for后 1,2,0 2,1,0,for后 2,0,1
调用函数从2开始 与m相等 输出后返回
关于汉诺塔
#include <stdio.h>
int cnt=0;
int dayin(int n,char a,char b)
{
cnt++;
printf("%d:第%d个从%c到%c\n",cnt,n,a,b);
}
int hannuota(int n,char a,char b,char c)
{
if(0==n)
{
return 0;
}
hannuota(n-1,a,c,b);
dayin(n,a,c);
hannuota(n-1,b,a,c);
}
int main()
{
int n;
char a='a',b='b',c='c';
printf("请输入汉诺塔层数");
scanf("%d",&n);
hannuota(n,a,b,c);
}
输出:
请输入汉诺塔层数3
1:第1个从a到c
2:第2个从a到b
3:第1个从c到b
4:第3个从a到c
5:第1个从b到a
6:第2个从b到c
7:第1个从a到c
汉诺塔的移动拆分:
从A移动第n个圈到C需要前两个圈都在B上,所以需要先移动第2个圈到B上,移动第二个圈需要第一个圈先在C上
也就是每个圈的上衣圈需要去除了自己所在和目标所在的另一个位置,也就是(n,1,2,3)中的2的位置。所以内部递归是(n-1,1,3,2);之后就可以打印第n个圈从哪里移动到哪里(为了显示正常的移动过程需要放在两次递归的中间),再之后为了给n的移动腾出位置,需要将第n-1个圈放到n的目标位置(n-1,2,1,3);