总言
比较巧妙的思路,对初学者的我来说值得学习。
会慢慢学习和补充。
文章目录
一、定义与基本运用
1)、定义:
递归即函数调用其本身,其主要思想在于把大事化小,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
类似于一个深层的嵌套。
2)、递归的两个使用条件
递归需要限制条件,符合限制条件后,递归停止。
每次递归调用,都要朝这一限制条件发展。
二、例子解析
1)、main函数自己调用其本身
一个简单的递归,但是一种错误的写法,原因在于没有遵循上述两条原则,程序最终会因栈溢出而崩溃。
//main函数自己调用自己
#include<stdio.h>
int main(void)
{
printf("hehe\n");
main();
return 0;
}
2)、整数正序打印数位值
题目:接受一个整型值(无符号),按照顺序打印它的每一位。 例如: 输入:1234,输出 1 2 3 4.
整体思路:
要正位打印每一个数,可将其不同数位上的数一一剥离。通常来说十进制下,/10、%10能做得到。但如果直接运用,则是逆序打印。如果考虑到递归,即让函数调用其本身,就需要考虑递归的两个条件。
(个人感觉此处缺少关于递归与问题的实际建立。)
此题中,对1234,采用递归的方式,我们可先剥离末位数,对前者再进行递归调用。即f(1234)=f(123)+4。
由于一次函数调用中,含有需要打印数和另一递归调用,因此程序会继续分析包含其中的函数调用,即f(123)=f(12)+3。以此类推,得f(12)=f(1)+2,f(1)=1;程序执行到此步,堆积起的函数调用终于能依次解开。
即函数逐一返回原先调用的位置,因此程序先打印1,再接着打印2、3、4.
代码如下:
#include<stdio.h>
void print(unsigned int n)
{
if (n > 9)
print(n / 10);
printf("%d ", n % 10);
}
int main(void)
{
unsigned int n;
scanf("%u", &n);
print(n);
return 0;
}
3)、模拟实现strlen:递归方式
题目:编写函数,不允许创建临时变量,求字符串的长度。
代码如下:
使用库函数的情况下:
#include <stdio.h>
#include <string.h>
int main(void)
{
char arr[] = "abcde";
printf("%d\n", strlen(arr));
return 0;
}
创建临时变量的情况:
int mystrlen(char* arr)
{
int count = 0;
while (*arr != '\0')
{
count++;
arr++;
}
return count;
}
int main(void)
{
char arr[] = "abcdef";
printf("%d", mystrlen(arr));
return 0;
}
使用递归的情况:
f(“abc”)=1+f(“bc”),f(“bc”)=1+f(“c”),f(“c”)=1+f(‘\0’),f(‘\0’)=1。
#include <stdio.h>
int mystrlen(char* arr)
{
if (*arr == '\0')
return 0;
return 1 + mystrlen(++arr);
}
int main(void)
{
char arr[] = "abcdef";
printf("%d", mystrlen(arr));
return 0;
}
4)、不考虑溢出情况,求n的阶乘
题目:不考虑溢出情况,求n的阶乘
代码如下:
//非递归方式:
#include<stdio.h>
int main(void)
{
int n;
scanf("%d", &n);
int sum = 1;
while (n > 0)
{
sum *= n;
n--;
}
return 0;
}
//用递归的方式:
int factorial(int n)
{
if (n <= 1)
return 1;
else
return n * factorial(n - 1);
}
int main(void)
{
int n;
scanf("%d", &n);
printf("%d", factorial(n));
return 0;
}
5)、不考虑溢出情况,求第n个斐波那契数
题目:不考虑溢出情况,求第n个斐波那契数
代码如下:
//以递归方式实现:
#include<stdio.h>
int fib(int n)
{
if (n <= 2)
return 1;
return fib(n - 1) + fib(n - 2);
}
int main(void)
{
int n;
scanf("%d", &n);
printf("%d", fib(n));
return 0;
}
//以非递归的方式实现:
#include<stdio.h>
int fib(int n)
{
int a = 1;
int b = 1;
int c = 1;//此处为c赋值为1不影响后续计算,因为计算时C=a+b
while (n - 2)
{
c = a + b;
a = b;
b = c;
n--;
}
return c;//为此处方便故给C赋值为1,这样就不用判断n<2的情况了。
}
int main(void)
{
int n;
scanf("%d", &n);
printf("%d", fib(n));
return 0;
}
衍生题:计算未知数到Fibonacci数列所需最小步数
- 思路分析:
从题目中我们需要知道什么?
1、此题需要用未知数与斐波那契数比较
2、这种比较可以往上增长和往下减小,即未知数在两个斐波那契数之间(闭区间) - 法一:
#include<stdio.h>
//一个用于实现斐波那契数的函数
int fibo(int x)
{
if (x <= 2)
return 1;
return fibo(x - 1) + fibo(x - 2);
}
int main()
{
int n=0;
scanf("%d", &n);
int a = 1, b = 1, i = 1;
while(i)
{
//a、b:相邻两斐波那契数
a = fibo(i);//用于得出小的斐波那契数
b = fibo(i + 1);//用于得出大的斐波那契数
if (n >= a && n <= b)//判断条件
{
printf("%d\n", ((n - a) > (b - n)) ? (b - n) : (n - a));
//注意此处大小判断,步数是绝对值,改进方法:绝对值函数abs,头文件:<math.h>
//printf("%d\n", (abs(n - a) > abs(n - b)) ? abs(n - b) : abs(n - a));
break;
}
i++;
};
return 0;
}
6)、递归实现字符串逆序
题目:编写一个函数 reverse_string(char * string)(递归实现)
实现:将参数字符串中的字符反向排列,不是逆序打印。
要求:不能使用C函数库中的字符串操作函数。
比如:
char arr[] = “abcdef”;
逆序之后数组的内容变成:fedcba
代码如下:
//满足不能用字符串操作函数的条件
int mystrlen(char* arr)
{
if (*arr == '\0')
return 0;
return 1 + mystrlen(arr + 1);
}
//非递归版:
void reverse1(char*arr,int sz)
{
char* left = arr;
char* right = arr + sz - 1;
while (left < right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
//递归版:
void reverse2(char* arr)
{
char tmp = *arr;//备份
int sz = mystrlen(arr);
*arr = *(arr + sz - 1);//首尾字符交换,此处括号不能省略
*(arr + sz - 1) = '\0';//为了后续递归时字符能找对
if (mystrlen(arr + 1) > 1)//递归停止条件:当f()中字符串不少于2个时,则再次调用函数交换。
reverse2(arr + 1);
*(arr + sz - 1) = tmp;
}
int main(void)
{
//非递归版
char arr[] = "abcdef";
reverse1(arr, mystrlen(arr));
printf("%s\n",arr);
//递归版:
//非递归版运行后得逆置后的字符串fedcba,则在递归版中将还原字符串。
reverse2(arr);
printf("%s\n", arr);
return 0;
}
7)、计算一个数的每位之和(递归实现)
题目:写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和
例如,调用DigitSum(1729),则应该返回1+7+2+9,它的和是19
输入:1729,输出:19
代码如下:
f(1729)=9+f(172);f(172)=2+f(17);f(17)=7+f(1)……
unsigned int digitsum (unsigned int n)
{
if (n > 9)//此处使用n>9是因为考虑到只有个位数的情况。
//直接使用n/10则个位出错。
return n % 10 + digitsum(n / 10);
return n;//个位返回值
}
int main(void)
{
unsigned int n;
scanf("%u", &n);
printf("%u", digitsum(n));
return 0;
}