函数递归(详细解读)(下)

简介: 函数递归(详细解读)(下)

2.3编写函数不允许创建临时变量,求字符串的长度。

尝试一下用递归的方式解决

思路:当发现str指向的字符不是'\0'时,长度至少为1, 总长度就是1加上后面字符串的长度

my_strlen("abcdef") <--> 1+my_strlen("bcdef")


画图解释:

5be531e5e3dc4b3cb9bef4b007fe3b73.png

为了便于理解,接下来简化一下字符串, 变为"abc"

int my_strlen(char* str)
{
  if(*str != '\0')
    return 1 + my_strlen(str + 1);
  else
      return 0;
}
int main()
{
  char arr[10] = "abc";
  int len = my_strlen(arr);
  printf("%d\n", len);
  return 0;
}


243558214c3d441bbdeda3b404279c48.png

       再次强调递归满足的两个条件:

1  满足语句条件,进入语句递归

2  以不断+1的方式逼近跳出的条件('\0')

if(*str != '\0')

    return 1 + my_strlen(str + 1);

   else

       return 0;


错误总结:

错误代码:

if(*str != '\0')

       return 1 + my_strlen(str++);

   else

       return 0;


原因

++是后置的,所以是先把str的地址传入函数,再++,是没用的,没有逼近跳出条件

每一个函数的str都是独立的,代码会死递归

图解:

2ca17d8f1c3c48b890652b2439f308b0.png

         str+1和++str写法的区别

1 my_strlen(str + 1) <--> 2 my_strlen(++str)

1 把下一个字符的地址传进去了,留下的str是不会动的,对于str是不会修改

2 留下的str动了,如果递归回来想使用这个原来的地址,那就不是原来需要的,就会出现问题


补充:

a995e398d5de4e919474bda30e4fb05e.png

总结:

1.内存分为一个个的内存单元,一个单元大小是byte

2.每个字节都有一个地址(地址之间相差1个字节)


递归与迭代


递归:是指函数/过程/子程序在运行过程序中直接或间接调用自身而产生的重入现象。

迭代:是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。


求n的阶乘。(不考虑溢出)


求阶乘的一种基础方法

fac(n)=n * fac(n-1)


图解:

0d5f9d3640ba41f49cb566a2dc96a49f.png

代码呈现:

int fac(int n)
{
  int i = 0;
  int ret = 1;
  for (i = 1; i <= n; i++)
  {
    ret = ret * i;
  }
  return ret;
}
int main()
{
  int n = 0;
  scanf("%d",&n);
  int ret = fac(n);
  printf("%d\n", ret);
  return 0;
}


程序运行:

b81a1211d9a84f67a072fda0c3176f47.png

求第n个斐波那契数。(不考虑溢出)

                                                       "斐波那契数列:"

1  1  2  3  5  8  13  21  34  55  .........


即当n>=2时,这个数列从第3项开始,每一项都等于前两项之和。

图解:

73e0677fd30945a78dd23dfb3397ab66.png

递归的方式实现斐波那契数列计算

//求第n个斐波那契数
int count;//统计第n个斐波那契数重复计算的次数
int Fib(int n)
{ if (n == 3)
    count++;
  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);
  printf("count=%d\n", count);
  return 0;
}

e71a22a17a7b4106926dc18f274348fe.png

count作用:

是用于计算第40(n输入的40)个斐波那契数时,统计第3个斐波那契数重复出现的次数

举例:

if (n == 3)

       count++;

printf("count=%d\n", count) ;

图解:

be67cb293efd48f2ac25e8d96e58cb1d.png

可以发现36重复出现多次,而且如果n输入50时特别耗费时间,(如果你的参数比较大)那就会报错: stack overflow(栈溢出) 这样的信息

因为:

系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一 直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称为栈溢出。


递归缺点:

输入50,计算第50个斐波那契数

4145309595bd4c72b6686f22e9603637.png

光标在动,说明程序还在运行。

打开任务管理器

cc8f398579a94f0c9588a7b91bd6e719.png

还在占用着内存,说明程序是正常运行的

总结:

递归可以解决问题,但是遇到数字大的时候解决问题的效率很低


寻求解决问题其他办法:


迭代

循环也是迭代的一种

//迭代的方式
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);
  printf("count=%d\n", count);
  return 0;
}


思路:

4b783dda00b047eda6cbbcdafd9c3cfb.png


写代码用递归还是迭代?

递归:写代码写起来很直接, 像公式一样写出来,代码没有明显问题

迭代:若递归写出来的效率太低,存在明显缺陷(斐波那契数列的问题用递归去写),则用迭代的方式提高效率

提示:

1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。

2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。

3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。

欢迎各位大佬补充,谢谢支持!

相关文章
|
算法
函数递归(详细解读)(上)
函数递归(详细解读)(上)
|
编译器
【函数和函数递归】
【函数和函数递归】
57 0
|
3月前
利用递归函数调用
利用递归函数调用。
35 9
|
2月前
函数的递归
函数的递归
18 0
|
4月前
|
算法
【算法】递归总结:循环与递归的区别?递归与深搜的关系?
【算法】递归总结:循环与递归的区别?递归与深搜的关系?
106 0
|
6月前
|
C语言
C语言函数递归详解:理解递归的原理与应用
C语言函数递归详解:理解递归的原理与应用
126 0
|
7月前
|
C语言
函数递归.
这篇内容介绍了递归的概念,指出在C语言中递归是函数自我调用。它通过一个简单的死递归示例展示了未设置停止条件会导致栈溢出。接着,文章阐述了递归的两个必要条件:存在限制条件以终止递归,以及每次递归调用都更接近这个限制条件。随后,文章通过计算阶乘和顺序打印整数位的例子展示了递归的应用,并对比了递归和迭代的效率,强调在存在冗余计算时,迭代通常比递归更高效。
37 0
|
7月前
|
机器学习/深度学习 算法
详解函数递归
详解函数递归
|
7月前
|
机器学习/深度学习 编译器 C语言
关于函数递归的基础
关于函数递归的基础
55 5
|
7月前
|
机器学习/深度学习
浅学函数递归
浅学函数递归