函数(3)

简介: 函数(3)

迭代(非递归):循环就是一种迭代,迭代可以理解成循环

我们什么时候优先用迭代呢?

(1)递归层次太深时,栈溢出程序会崩溃

(2)递归的过程中有很多计算在一直重复,程序效率低

7.3.1 练习3

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

代码1——用递归写

#include<stdio.h>
int Fac(int n)
{
  if (n <= 1)
  {
    return 1;
  }
  else
  {
    return n * Fac(n - 1);
  }
}
int main()
{
  int n = 0;
  scanf("%d", &n);
  int ret = Fac(n);
  printf("%d", ret);
  return 0;
}

《函数栈帧

    每一个函数调用都会为本次函数调用分配内存空间(是内存的栈区),为本次函数调用分配的内存空间就被称为这函数调用的栈帧空间。

函数栈帧的创建和递归!



递归层次太深,就会栈溢出程序崩溃!

例如:使用 factorial 函数求10000的阶乘(不考虑结果的正确性),程序会崩溃。

更改:代码2——迭代

#include<stdio.h>
int Fac(int n)
{
  int i = 0;
  int ret = 1;
  for (i = 1; i <= n; i++)
  {
    ret *= i;
  }
  return ret;
}
int main()
{
  int n = 0;
  scanf("%d", &n);
  int ret = Fac(n);
  printf("%d\n", ret);
  return 0;
}

好处:这时程序是不会栈溢出,也就不会崩溃!(虽然结果是错的,但是不会崩——int的数据范围小了,这讲的是思想)

当递归层次太深时——非递归


7.3.2  练习4

求第n个斐波那契数

代码1——递归

#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;
}

发现在使用 fib 这个函数的时候如果我们要计算第 50 个斐波那契数字的时候特别耗费时间

为什么呢?


我们发现 fib 函数在调用的过程中很多计算其实在一直重复。

我们更改一下代码——看看第3个斐波那契数被重复计算了多少次


#include<stdio.h>
int count = 0;
int Fib(int n)
{
  if (3 == n)//统计第三个斐波那契数被计算的次数
  {
    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("%d\n", count);
  return 0;
}


从代码结果:第3个斐波那契数重复了39088169次,——效率低

改成迭代

代码2——迭代

减少了重复运算,提高了效率

#include<stdio.h>
int Fib(int n)
{
  int a = 1;
  int b = 1;
  int c = 1;
  while(n>2)
  {
    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. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。

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

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


递归存在的问题:

(1)递归层次太深时, 那就会报错: stack overflow (栈溢出) 这样的信息。

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

(2)递归的过程中存在大量的重复计算(效率低)

那如何解决上述的问题:

1. 将递归改写成非递归。(迭代就是非递归)

2. 使用 static 对象替代 nonstatic 局部对象。在递归函数设计中,可以使用 static 对象替代

nonstatic 局部对象(即栈对象),这不仅可以减少每次递归调用和返回时产生和释放nonstatic 对象的开销,而且 static 对象还可以保 存递归调用的中间状态,并且可为各个调用层所访问。(static是存放在静态区的)


相关文章
|
2月前
|
安全 C语言
C 安全函数
C 安全函数。
36 3
|
8月前
|
存储 编译器 C++
|
7月前
|
算法 Java 开发者
解密CollectGarbage函数
解密CollectGarbage函数
|
7月前
|
C++
<iomanip>库中setw(),setfill()等函数的使用
<iomanip>库中setw(),setfill()等函数的使用
184 0
|
8月前
|
Java 测试技术 Python
为什么要用函数
在编程中,函数是一种重要的抽象工具,它使我们能够组织和复用代码,提高代码的可读性、可维护性和效率。函数允许我们将一段代码块封装起来,给它一个名字,并通过参数和返回值来与外部世界交互。下面,我们将深入探讨为什么要使用函数,并附上相应的代码示例。
127 1
|
C语言
C语言知识点之 函数2
C语言知识点之 函数2
53 0
|
存储 C语言
对函数的剖析二
对函数的剖析二
63 0
|
算法 程序员 信息无障碍
从零带你认识函数(二)
从零带你认识函数
98 1
|
存储 编译器 C语言
C语言知识点之 函数
C语言知识点之 函数
66 0
|
自然语言处理 C++
C/C++ 中的 atol()、atoll() 和 atof() 函数
1.atol(): 此函数将作为参数传递给函数调用的 C 类型字符串转换为长整数。它解析 C 字符串 str 并将其内容解释为整数,该整数作为 long int 类型的值返回。该函数会丢弃字符串开头的空白字符,直到找到非空白字符。如果 C 字符串 str 中的非空白字符序列不是有效的整数,或者如果因为 str 为空或仅包含空白字符而不存在这样的序列,则不执行任何转换并返回零。
267 0