1 递归是什么?
递归是一种解决问题的方法,在C语言中,递归就是函数自己调用自己。
1.1 递归的思想
把一个大型复杂问题层层转化为一个与原问题相似,但规模较小的子问题来求解;直到子问题不能再被拆分,递归就结束了。所以递归的思考方式就是把大事化小的过程。
递归中的递就是递推的意思,归就是回归的意思,接下来慢慢来体会。
1.2 递归的限制条件
递归在书写的时候,有2个必要条件:
- 递归存在限制条件,当满足这个限制条件的时候,递归便不再继续。
- 每次递归调用之后越来越接近这个限制条件。
在下面的例子中,我们逐步体会这2个限制条件。
2 递归题目解析
2.1 求n的阶乘
一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。
自然数n的阶乘写作n!。
题目:计算n的阶乘(不考虑溢出),n的阶乘就是1~n的数字累积相乘。
2.1.1 分析和代码实现
我们知道n的阶乘的公式:n! = n * (n一1)!
从这个公式不难看出:如何把一个较大的问题,转换为一个与原问题相似,但规模较小的问题来求解的。
n的阶乘和n-1的阶乘是相似的问题,但是规模要少了n。有一种有特殊情况是:当n==0的时候,n的阶乘是1,而其余n的阶乘都是可以通过上面的公式计算。
这样就能写出n的阶乘的递归公式:
那我们就可以写出函数Fact()求n的阶乘(递归是基于函数的),假设Fact(n)就是求n的阶乘,那么Fact(n-1)就是求n-1的阶乘,函数如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//递归
int Func(int n)
{
if (n == 0)
return 1;
return n * Func(n - 1);
}
//非递归
int Func2(int n)
{
int m = 1;
for (int i = 1; i <= n; i++)
{
m *= i;
}
return m;
}
int main()
{
int n = 5;
printf("%d\n", Func(n));
printf("%d\n", Func2(n));
return 0;
}
2.2 顺序打印一个整数的每一位
输入一个整数m,按照顺序打印整数的每一位。
比如:
输入:1234 输出:1 2 3 4
输入:520 输出:5 2 0
2.2.1 分析和代码实现
这个题目,放在我们面前,首先想到的是,怎么得到这个数的每一位呢?
如果n是一位数,n的每一位就是n自己
n是超过1位数的话,就得拆分每一位
1234%10就能得到4,然后1234/10得到123,这就相当于去掉了4
然后继续对123%10,就得到了3,再除10去掉3,以此类推
不断的%10和/10操作,直到1234的每一位都得到;
但是这里有个问题就是得到的数字顺序是倒着的
但是我们有了灵感,我们发现其实一个数字的最低位是最容易得到的,通过%10就能得到
那我们假设想写一个函数Print()来打印n的每一位,如下表示:
如果n是1234,那表示为
Print(1234) //打印1234的每一位
其中1234中的4可以通过%10得到,那么
Print(1234)就可以拆分为两步:
-
Print(1234/10)//打印123的每一位 -
printf(1234%10) //打印4
完成上述2步,那就完成了1234每一位的打印
那么Print(123)又可以拆分为Print(123/10) + printf (123%10)
以此类推下去,直到被打印的数字变成一位数的时候,就不需要再拆分,递归结束。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//打印一个数的每一位
void Print(int n)
{
if (n > 9)
{
Print(n / 10);
}
printf("%d ", n%10);
}
int main()
{
int n = 1234;
Print(n);
return 0;
}
在这个解题的过程中,我们就是使用了大事化小的思路
把Print(1234)打印1234每一位,拆解为首先Print(123)打印123的每一位,再打印得到的4
把Print(123)打印123每一位,拆解为首先Print(12)打印12的每一位,再打印得到的3
直到Print打印的是一位数,直接打印就行。
2.3 青蛙跳台阶问题[1]
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
2.3.1 分析和代码实现
我们设定Jump(n)可以计算青蛙跳上一个n级的台阶总共有多少种跳法。
假设n = 4,青蛙跳到第4级台阶时有两种跳法,从第3级跳上来或者从第2级跳上来
可以写作Jump(4) = Jump(3) + Jump(2)
而青蛙跳到第3级台阶时也有两种跳法,从第2级跳上来或者从第1级跳上来
Jump(3) = Jump(2) + Jump(1)
我们知道当台阶为1级时,只有1种跳法;为2级时,有2种跳法。
即 Jump(2) = 2;Jump(1) = 1
接下来我们推广到第n级(n>2)台阶时前一次跳的情况。它也有两种跳法:一是从第n-1级台阶起跳,二是从第n-2级台阶起跳。这样一来,我们就可以写出关于n的递推公式:
那我们就可以写出函数Jump()求n级台阶的跳法,假设Jump(n)就是到n级台阶的跳法,那么Jump(n-1)就是求到n-1级台阶的跳法,Jump(n-2)就是求到n-2级台阶的跳法,函数如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//青蛙跳台阶
int Jump(int x)
{
if (x == 1)
return 1;
else if (x == 2)
return 2;
else
return Jump(x - 1) + Jump(x - 2);
}
int main()
{
int n = 4;
int ret = Jump(n);
printf("%d\n", ret);
return 0;
}
2.4 计算一个数的每位之和
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//计算一个数的每位之和
int DigitSum(int n)
{
if (n < 10)
{
return n;
}
return n%10 + DigitSum(n/10);
}
int main()
{
int n = 1234;
printf("%d", DigitSum(n));
return 0;
}
2.5 计算x的y次幂
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//计算x的y次幂
int MI(int x, int y)
{
if (y == 0)
return 1;
else if (y == 1)
return x;
else
return x * MI(x, y - 1);
}
int main()
{
int x = 0;
int y = 0;
scanf("%d %d", &x, &y);
int ret = MI(x, y);
printf("%d\n", ret);
return 0;
}
2.6 汉诺塔问题[2]
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//汉诺塔
void Print(char x, char y)
{
printf("%c->%c ", x, y);
}
void Hanno(int x, char pos1, char pos2, char pos3)
{
if (x == 1)
Print(pos1, pos3);
else
{
Hanno(x - 1, pos1, pos3, pos2);
Print(pos1, pos3);
Hanno(x - 1, pos2, pos1, pos3);
}
}
int main()
{
int n = 3;
Hanno(n,'A','B','C');
return 0;
}
正文完
参考资料:
[1]青蛙跳台阶问题: www.nowcoder.com/share/jump/…
[2]汉诺塔问题: www.nowcoder.com/share/jump/…
C语言实现汉诺塔: www.bilibili.com/video/BV13g…