目录
一 函数递归
1 递归介绍
一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复 杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可 描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归的主要思考方式在于:把大事化小
递归的两个必要条件
1 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
2 每次递归调用之后越来越接近这个限制条件。
#include<stdio.h>
int main()// 这是一个最简单的递归
{
printf("haha\n");
main();// 无线循环下去 成为死递归
return 0;
}
2 例一 顺序打印
//接受一个整型值(无符号),按照顺序打印它的每一位。 例如: 输入:1234,输出 1 2 3 4
#include<stdio.h>
void print(unsigned int x)
{
if (x > 9)// 如果不加限制条件 递归会无限开辟栈区空间 栈区空间是有限的 最后会栈溢出
{
print(x / 10);
}
printf("%d ", x % 10);
}
int main()
{
// 1234%10 == 4
// 123%10 == 3;
// 12%10 == 2;
// 1 % 10 == 1;
unsigned int a = 0;
scanf("%d", &a);
print(a);
return 0;
}
分析一下
递归 就是递推和回归 蓝色是递推 黄色就是回归
那红色数字 1234 123 12 1 这些数字是如何保存的?
那就考虑函数的调用堆栈或者函数栈帧
2 例二 求字符串长度
// 求字符串长度
int my_strlen(char* s)
{
int count = 0;
while (*s != '\0')
{
count++;
s++;
}
return count;
}
int main()
{
char arr[] = "abc";
// [a b c \0]
int len = my_strlen(arr);// 数组名是首元素的地址 第一个元素的地址
printf("%d\n", len);
return 0;
}
//编写函数不允许创建临时变量,求字符串的长度。不允许创建count
//递归
#include<stdio.h>
#include<string.h>
int my_strlen(char* s)
{
if (*s == '\0')// *s 拿到的是初始地址 也就是第一个地址 a 的地址
return 0;
else
return 1 + my_strlen(s + 1);// s + 1 就是地址(指针)往后偏移 拿后面的地址 s指向a s+1就指向 b
// my_strlen("abc")
// 1+my_strlen("bc")
//1+1+my_strlen("c")
//1+1+1+my_strlen("")
//1+1+1+0
//3
}
int main()
{
char arr[] = "abc";
// [a b c \0]
int len = my_strlen(arr);// 数组名是首元素的地址 第一个元素的地址
printf("%d\n", len);
return 0;
}
分析一下
二 递归与迭代
1 求n的阶乘(不考虑溢出)
#include<stdio.h>
// 循环(迭代)
int fun(int n)
{
int i = 0;
int r = 1;
for (i = 1; i <= n; i++)
{
r = r * i;
}
return r;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = fun(n);
printf("%d\n", ret);
return 0;
}
#include<stdio.h>
//递归
int fun(int n)
{
if (n <= 1)
return 1;
else
return n * fun(n - 1);
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = fun(n);
printf("%d\n", ret);
return 0;
}
2 求第n个斐波那契数。(不考虑溢出)
//求第n个斐波那契数。(不考虑溢出)
// 1 1 2 3 5 8 13 .....
//前两个数的和是第三个数
#include<stdio.h>
int fib(int n)
{
if (n <= 2)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = fib(n);
printf("%d\n", ret);
return 0;
}
但是有个问题 我们输入数值较大的时候 计算机就吃不消了 虽然一直在跑 但是迟迟算不出来结果
所以我们就要考虑迭代的方法了
#include<stdio.h>
// 循环(迭代)
int fib(int n)
{
int a = 1;
int b = 1;
int c = 1;
while (n >= 3)
{
c = a + b;
a = b;
b = c;
n--;
}
return c;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = fib(n);
printf("%d\n", ret);
return 0;
}
1 + 1 = 2 (第三个数)
a + b = c
1 + 2 = 3 (第四个数)
b赋给a c赋给b
2 + 3 = 5 (第五个数)
..........
1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。
2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开 销。