5 函数(递归部分)
- 格式:ppt
- 大小:270.00 KB
- 文档页数:12
函数的定义与使用函数的定义与使用函数是计算机编程中的重要概念,作为一种可重复使用的代码块,函数能够接受输入参数并返回输出结果,从而可以简化程序的编写,提高程序的可读性和可维护性。
本文将简要介绍函数的定义与使用。
一、函数的定义函数是一段预定的代码块,用于完成特定的任务或计算。
函数通常由输入参数、函数体和输出结果三部分组成。
其中输入参数用于传递外部数据给函数,函数体是实现具体功能的代码,输出结果则是函数执行完毕后返回给调用者的值。
函数的定义通常由以下几个部分组成。
1.函数名函数名是函数的唯一标识符,用于区分不同的函数。
函数名通常使用有意义的英文单词或短语,以便于程序员理解和记忆。
函数名的命名应该遵循编程语言的命名规范,通常采用驼峰法(Camel Case)或下划线法(Snake Case)。
2.函数参数函数参数是函数输入的数据。
参数可以是任何数据类型,包括基本类型和自定义类型。
函数可以有一个或多个参数,参数之间以逗号分隔。
参数可以有默认值,如果在调用函数时不指定参数值,则使用默认值。
例如,在Python中定义一个名为“add”的函数,其参数为两个整数a和b,函数实现为返回a+b的和,如下所示。
def add(a=0, b=0):return a + b3.函数返回值函数返回值是函数执行完毕后返回的结果。
返回值可以是任何数据类型,包括基本类型和自定义类型。
函数可以返回一个或多个返回值,通过在函数体中使用return语句来指定。
如果函数没有返回值,则返回None。
例如,在Python中定义一个名为“calculate”的函数,其参数为两个整数a和b,函数实现为返回a+b和a-b的结果,如下所示。
def calculate(a, b):return a+b, a-b二、函数的使用函数的使用具有很高的灵活性,可以在不同的环境和场景中使用。
下面介绍几种常见的函数使用方法。
1.函数的定义和调用函数的定义包括函数名、函数参数和函数体,可以在代码的任何位置定义。
1 如果 n=0,n=1f(n)=nf(n) 如果 n>1图1:以递归的方式计算4的阶乘上图(1)展示了利用递归计算4!的过程。
它也说明了递归过程中的两个基本阶段:递推和回归。
在递推阶段,每一个递归调用通过进一步调用自己来记住这次递归过程。
当其中有调用满足终止条件时,递推结束。
比如,在计算n!时,终止条件是当n=1和n=0,此时函数只需简单的返回1即可。
每一个递归函数都必须拥有至少一个终止条件;否则递推阶段永远不会结束了。
一旦递推阶段结束,处理过程就进入回归阶段,在这之前的函数调用以逆序的方式回归,直到最初调用的函数为止,此时递归过程结束。
以递归的方式计算n的阶乘的函数实现:C函数fact的工作方式如下:它接受一个整数n作为参数,如果n小于0,该函数直接返回0,这代表一个错误。
如果n等于0或1,该函数返回1,这是因为0!和1!都等于1,以上是终止递归的条件。
否则,函数返回n-1的阶乘的n倍。
而n-1的阶乘又会以递归的方式再次调用fact来计算,如此继续。
代码实例(1):fact.c1/*fact.c*/2#include "fact.h"3int fact(int n){4if (n<0)5return0;6else if(n==0)7return1;8else if(n==1)9return1;10else11return n*f(n-1);12}为理解递归究竟是如何工作的,有必要先看看C语言中函数的执行方式。
我们先来看看C程序在内存中的组织方式(见图2-a)。
基本上,一个可执行程序由4个区域组成:代码段、静态数据区、堆与栈。
代码段包含程序运行时所执行的机器指令。
静态数据区包含在程序生命周期内一直持久的数据,比如全局变量和静态局部变量。
堆包含程序运行时动态分配的存储空间,比如malloc分配的内存。
栈包含函数调用的信息。
当C中调用了一个函数时,栈中会分配一块空间来保存与这个调用相关的信息。
数据结构之递归Ⅲ递归的三⼤要素// 算 n 的阶乘(假设n不为0)int f(int n){if(n <= 2){return n;}}第三要素:找出函数的等价关系式第三要素就是,我们要不断缩⼩参数的范围,缩⼩之后,我们可以通过⼀些辅助的变量或者操作,使原函数的结果不变。
例如,f(n) 这个范围⽐较⼤,我们可以让 f(n) = n * f(n-1)。
这样,范围就由 n 变成了 n-1 了,范围变⼩了,并且为了原函数f(n) 不变,我们需要让 f(n-1) 乘以 n。
说⽩了,就是要找到原函数的⼀个等价关系式,f(n) 的等价关系式为 n * f(n-1),即f(n) = n * f(n-1)。
这个等价关系式的寻找,可以说是最难的⼀步了,如果你不⼤懂也没关系,因为你不是天才,你还需要多接触⼏道题,我会在接下来的⽂章中,找 10 道递归题,让你慢慢熟悉起来。
找出了这个等价,继续完善我们的代码,我们把这个等价式写进函数⾥。
如下:// 算 n 的阶乘(假设n不为0)int f(int n){if(n <= 2){return n;}// 把 f(n) 的等价操作写进去return f(n-1) * n;}⾄此,递归三要素已经都写进代码⾥了,所以这个 f(n) 功能的内部代码我们已经写好了。
这就是递归最重要的三要素,每次做递归的时候,你就强迫⾃⼰试着去寻找这三个要素。
还是不懂?没关系,我再按照这个模式讲⼀些题。
有些有点⼩基础的可能觉得我写的太简单了,没耐⼼看?少侠,请继续看,我下⾯还会讲如何优化递归。
当然,⼤佬请随意,可以直接拉动最下⾯留⾔给我⼀些建议,万分感谢!Ⅲ案例1:斐波那契数列斐波那契数列的是这样⼀个数列:1、1、2、3、5、8、13、21、34....,即第⼀项 f(1) = 1,第⼆项 f(2) = 1.....,第 n 项⽬为 f(n) = f(n-1) + f(n-2)。
求第 n 项的值是多少。
递归函数python经典例子递归函数是编程中经常使用的一种技巧,它可以让函数在内部调用自身来实现复杂的逻辑。
下面是十个经典的递归函数示例,展示了不同场景下递归函数的应用。
1. 阶乘函数阶乘函数是递归函数的经典示例。
它用于计算一个整数的阶乘,即n! = n * (n-1) * (n-2) * ... * 1。
递归版本的阶乘函数可以通过将问题拆分为更小的子问题来解决。
```pythondef factorial(n):if n == 0:return 1else:return n * factorial(n-1)```2. 斐波那契数列斐波那契数列是一个经典的递归问题,定义如下:F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2)。
递归函数可以用来计算斐波那契数列的第n 个数。
```pythondef fibonacci(n):if n <= 1:return nelse:return fibonacci(n-1) + fibonacci(n-2)```3. 数组求和递归函数可以用来计算一个数组中所有元素的和。
可以将数组分为第一个元素和剩余部分,然后递归地计算剩余部分的和。
```pythondef array_sum(arr):if len(arr) == 0:return 0else:return arr[0] + array_sum(arr[1:])```4. 列表反转递归函数可以用来反转一个列表。
可以将列表分为第一个元素和剩余部分,然后递归地反转剩余部分,并将第一个元素放在列表的末尾。
```pythondef reverse_list(lst):if len(lst) <= 1:return lstelse:return reverse_list(lst[1:]) + [lst[0]]```5. 字符串反转递归函数可以用来反转一个字符串。
可以将字符串分为第一个字符和剩余部分,然后递归地反转剩余部分,并将第一个字符放在字符串的末尾。
函数的递归调用1.递归基本概念所谓递归,简而言之就是应用程序自身调用自身,以实现层次数据结构的查询和访问。
递归的使用可以使代码更简洁清晰,可读性更好。
但由于递归需要系统堆栈,所以空间消耗要比非递归代码要大很多,而且,如果递归深度太大,可能系统资源会不够用。
从理论上说,所有的递归函数都可以转换为迭代函数,反之亦然,然而代价通常都是比较高的。
但从算法结构来说,递归声明的结构并不总能够转换为迭代结构,原因在于结构的引申本身属于递归的概念,用迭代的方法在设计初期根本无法实现,这就像动多态的东西并不总是可以用静多态的方法实现一样。
这也是为什么在结构设计时,通常采用递归的方式而不是采用迭代的方式的原因,一个极典型的例子类似于链表,使用递归定义及其简单,但对于内存定义(数组方式)其定义及调用处理说明就变得很晦涩,尤其是在遇到环链、图、网格等问题时,使用迭代方式从描述到实现上都变得不现实。
因而可以从实际上说,所有的迭代可以转换为递归,但递归不一定可以转换为迭代。
2.C语言中的函数可以递归调用可以直接(简单递归)或间接(间接递归)地自己调自己。
这里我们只简单的谈谈直接递归。
采用递归方法来解决问题,必须符合以下三个条件:(1)可以把要解决的问题转化为一个新问题,而这个新的问题的解决方法仍与原来的解决方法相同,只是所处理的对象有规律地递增或递减。
说明:解决问题的方法相同,调用函数的参数每次不同(有规律的递增或递减),如果没有规律也就不能适用递归调用。
(2)可以应用这个转化过程使问题得到解决。
说明:使用其他的办法比较麻烦或很难解决,而使用递归的方法可以很好地解决问题。
(3)必定要有一个明确的结束递归的条件。
说明:一定要能够在适当的地方结束递归调用。
不然可能导致系统崩溃。
3.递归实例下面用一个实例来说明递归调用。
使用递归的方法求n!当n>1时,求n!的问题可以转化为n*(n-1)!的新问题。
比如n=5:第一部分:5*4*3*2*1 n*(n-1)!第二部分:4*3*2*1 (n-1)*(n-2)!第三部分:3*2*1 (n-2)(n-3)!第四部分:2*1 (n-3)(n-4)!第五部分:1 (n-5)! 5-5=0,得到值1,结束递归。
c语言函数求解技巧在C语言中,函数是一种非常重要的概念。
它允许我们将代码模块化并重复使用。
然而,有一些技巧可以帮助我们更有效地使用C语言函数。
本文将介绍一些C语言函数求解的技巧。
1. 函数的参数传递方式在C语言中,函数可以通过值传递或指针传递来传递参数。
对于简单的数据类型,如int、float等,通常使用值传递。
这意味着函数会创建参数的副本,并在函数内部使用这些副本。
对于复杂的数据类型,如数组或结构体,通常使用指针传递。
这可以避免复制大量数据,提高程序的效率。
2. 函数返回值函数可以返回一个值,这个值可以是任何数据类型,包括整数、浮点数、指针等。
函数的返回值通常用于表示函数的执行结果或计算结果。
我们可以利用函数的返回值来进行错误检测或进行进一步的计算。
3. 局部变量和全局变量在函数中,我们可以定义局部变量和全局变量。
局部变量只在函数内部可见,函数外部无法访问。
局部变量的作用域仅限于该函数内部。
全局变量在整个程序中都可见,可以在多个函数中使用。
全局变量的作用域从变量声明的地方开始,一直到程序的结束。
4. 函数指针在C语言中,我们可以定义指向函数的指针。
函数指针可以存储函数的地址,并且可以像函数一样调用。
函数指针可以用于实现函数回调、动态加载函数等功能。
通过使用函数指针,我们可以更灵活地编写代码,实现更复杂的算法。
5. 递归函数递归是一种函数调用自身的技术。
递归函数可以解决许多种类的问题,例如计算阶乘、斐波那契数列等。
递归函数通常包含两个部分:基本情况和递归情况。
基本情况是递归函数停止递归的条件,递归情况是递归函数继续递归调用自身的条件。
6. 内联函数内联函数是一种特殊类型的函数,它的定义和调用会被编译器进行优化,以减少函数调用的开销。
编译器会将内联函数的代码插入到每个调用它的地方,而不是通过函数调用的方式。
内联函数通常适用于函数代码较短的情况下,可以提高程序的执行效率。
7. 预处理器宏预处理器宏是一种在编译期间进行文本替换的机制。
前言说白了递归就象我们讲的那个故事:山上有座庙,庙里有个老和尚,老和尚在讲故事,它讲的故事是:山上有座庙,庙里有个老和尚,老和尚在讲故事,它讲的故事是:……也就是直接或间接地调用了其自身。
就象上面的故事那样,故事中包含了故事本身。
因为对自身进行调用,所以需对程序段进行包装,也就出现了函数。
函数的利用是对数学上函数定义的推广,函数的正确运用有利于简化程序,也能使某些问题得到迅速实现。
对于代码中功能性较强的、重复执行的或经常要用到的部分,将其功能加以集成,通过一个名称和相应的参数来完成,这就是函数或子程序,使用时只需对其名字进行简单调用就能来完成特定功能。
例如我们把上面的讲故事的过程包装成一个函数,就会得到:void Story(){puts("从前有座山,山里有座庙,庙里有个老和尚,老和尚在讲故事,它讲的故事是:");getchar();//按任意键听下一个故事的内容Story(); //老和尚讲的故事,实际上就是上面那个故事}函数的功能是输出这个故事的内容,等用户按任意键后,重复的输出这段内容。
我们发现由于每个故事都是相同的,所以出现导致死循环的迂回逻辑,故事将不停的讲下去。
出现死循环的程序是一个不健全的程序,我们希望程序在满足某种条件以后能够停下来,正如我们听了几遍相同的故事后会大叫:“够了!”。
于是我们可以得到下面的程序:#include<stdio.h>const int MAX = 3;void Story(int n);//讲故事int main(void){Story(0);getchar();return 0;}void Story(int n){if (n < MAX){puts("从前有座山,山里有座庙,庙里有个老和尚,老和尚对小和尚说了一个故事:");getchar();Story(n+1);}else{printf("都讲%d遍了!你烦不烦哪?\n", n);return ;}}上面的Story函数设计了一个参数n,用来表示函数被重复的次数,当重复次数达到人们忍受的极限(MAX次)时,便停下来。