函数与递归
Posted 小希望731
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数与递归相关的知识,希望对你有一定的参考价值。
目录
例题1:根据下面递归函数:调用函数Fun(2),返回值是多少?
例题 3编写一个函数 reverse_string(char * string)(递归实现)
例题4 写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和
1、. 函数的嵌套调用和链式访问
1.1函数的嵌套调用
函数与函数之间可以嵌套调用,但不可以嵌套定义。我们来仔细分析以下几个例子。
#include <stdio.h>
void test2()
{
printf("MIT\\n");
}
void test1()
{
int i = 0;
for (i = 0; i < 3; i++)
{
test2();//在test1函数中调用test2函数;
}
}
int main()
{
test1();//调用test1函数;
return 0;
}
可以看到,主函数调用test1 而test1函数循环调用了test2函数打印MIT 也就是说 在函数体内部调用其他自定义函数,就是所谓的函数嵌套调用。但函数不能嵌套定义,以下是错误例子。
1.2函数的链式访问
把一个函数的返回值作为另外一个函数的参数,就是链式访问,我们来看几个例子。
#include<stdio.h>
#include<string.h>
int main()
{
printf("%d", strlen("abc"));//strlen("abc")的返回值作为printf函数的参数
return 0;
}
来思考以下这段代码
#include<stdio.h>
#include<string.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}//这段代码的结果是4321 需要注意的是,printf函数以它打印成功的字符个数作为返回值 具体分析过程如下。
2、函数的声明与定义
2.1函数的声明
1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。
2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。
3. 函数的声明一般要放在头文件中的。
2.2 函数定义
函数的定义是指函数的具体实现,交待函数的功能实现。
3、函数递归
3.1递归的定义
程序调用自身的编程技巧称为递归,其核心思想就是分解,把大事层层剥离,大事化小。
3.2递归的条件
1、具有限制条件,当不满足这个限制条件时退出递归。
2、每次递归调用之后越来越接近这个限制条件。
下面我们通过几个例子来理解递归。
例题1:根据下面递归函数:调用函数Fun(2),返回值是多少?
int Fun(int n)
{
if(n==5)
return 2;
else
return 2*Fun(n+1);
}
分析过程如下:
Fun(2)--->返回16
return 2*Fun(3) 2*8=16
|__Fun(3):8
return 2*Fun(4) 2*4=8
|__Fun(4):4
return 2*Fun(5) 2*2=4
|__Fun(5):2
return 2
例题2:递归方式实现打印一个整数的每一位
void Print(unsigned int n)
{
if (n > 9)
{
Print(n / 10);//递归过程,自己调用自己。
}
printf("%d ", n % 10);//要注意这里不能加上else
}
int main()
{
unsigned int num = 0;
scanf("%u",&num);
Print(num);//对这个函数具体的递归过程,我们可以用数字123理解 具体分析如下图
return 0;
}
3.3递归与迭代
我们先通过几个例子来理解。
例题1 求n的阶乘
int Fun(int n)//递归实现求n的阶乘
{
if (n <= 1)
return 1;
else
return n * Fun(n - 1);
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = 1;
ret = Fun(n);
printf("%d\\n", ret);
}
例题2 求第n个斐波那契数列
int Fci(int n)//递归方式实现斐波那契数列
{
if (n <= 2)
return 1;
else
return Fci(n - 1) + Fci(n - 2);
}
int main()
{
int n = 0;
int ret = 0;
while (~scanf("%d", &n))
{
ret = Fci(n);
printf("ret=%d\\n", ret);
}
return 0;
}
但是,以上两种实现方式,均是不考虑n非常大的情况,如果n非常大,则会导致栈溢出。我们可以通过一个例子来理解一下。例如,我们可以来计算一下 当n=40的时候,第三个斐波那契数被重复计算了几次。
可见,当n非常大的时候,程序进行了大量重复计算,效率比较低。那我们该怎么优化这个代码呢?这时候我们想到迭代。以下是通过迭代的方法来实现斐波那契数列。
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;
int ret = 0;
while (~scanf("%d", &n))
{
ret = Fib(n);
printf("ret=%d\\n", ret);
}
return 0;
}
例题 3编写一个函数 reverse_string(char * string)(递归实现)
实现:将参数字符串中的字符反向排列,不是逆序打印。
**要求:**不能使用C函数库中的字符串操作函数。
void reverse_string(char* arr)
{
char *left = arr;
char *right = arr+strlen(arr)-1;//这是用非递归方式来实现。
while(left<right)
{
char t = *left;
*left = *right;
*right = t;
left++;
right--;
}
}
实现思路就是创建两个指针(首元素跟最后一个元素);left作为左端点,right作为右端点。首末两个元素交换,则通过创立临时变量。因为不是一次交换,所以需要一个循环,循环的条件是左端点小于右端点。
int my_strlen(char* str)
{
int count = 0;
while (*str != '\\0')
{
count++;
str++;
}
return count;
}
void reverse_string(char* str)
{
char tmp = *str;
int len = my_strlen(str);
*str = *(str + len - 1);//递归方式来实现。
*(str + len - 1) = '\\0';
if (my_strlen(str + 1) >= 2)
{
reverse_string(str + 1);
}
*(str + len - 1) = tmp;
}
int main()
{
char arr[] = "abcdef";
reverse_string(arr);
printf("%s\\n", arr);
return 0;
}
对于字符串“abcdef”,递归实现的大概原理: 1. 交换a和f, 2. 在两个字符交换后,再看成对中间四个字符实现逆序,大事化小。具体过程如下
1、创立一个临时变量,把a赋给这个变量;
2、把f付给a原来所在的位置;
3、将原来f所在位置赋值为'\\0',因为如果不改成\\0而将a直接传在这个位置,在下一次逆序时,就变成bcdea的逆序了。
4、将bcde看成要逆序的内容;
5、a移到f原来的位置。
具体原理如下
例题4 写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和
例如,调用DigitSum(1729),则应该返回1+7+2+9,它的和是19
输入:1729,输出:19
int DigitSum(int n)
{
if (n > 9)
return DigitSum(n / 10) + n % 10;//n%10是为了获得这个数字的每一位
else
return n;
}
int main()
{
int n = 0;
int ret = 0;
while (~scanf("%d", &n))
{
ret = DigitSum(n);
printf("ret=%d\\n", ret);
}
return 0;
}
对DigitSum(1729),我们采用大事化小思路,可以看成DigitSum(172)+9,而DigitSum(172)则又可以分解为DigitSum(17)+2+9 以此类推。因此 在DigitSum(n / 10) + n % 10中,DigitSum(n / 10)可以实现 DigitSum(172) DigitSum(17) 而n%10则获得了每一位数字。
以上是关于函数与递归的主要内容,如果未能解决你的问题,请参考以下文章