函数与递归

Posted 小希望731

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数与递归相关的知识,希望对你有一定的参考价值。

目录

1、. 函数的嵌套调用和链式访问

1.1函数的嵌套调用

 1.2函数的链式访问

 2、函数的声明与定义

2.1函数的声明

2.2 函数定义 

3、函数递归

3.1递归的定义

3.2递归的条件

例题1:根据下面递归函数:调用函数Fun(2),返回值是多少?

例题2:递归方式实现打印一个整数的每一位

 3.3递归与迭代

例题1 求n的阶乘

例题2 求第n个斐波那契数列

例题 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则获得了每一位数字。

以上是关于函数与递归的主要内容,如果未能解决你的问题,请参考以下文章

Python函数

我在 laravel 中的递归函数不调用自己

JavaScript 代码片段

vue递归组件的一些理解

代码复用与函数递归

哈斯克尔。我很困惑这个代码片段是如何工作的