C ~ 递归

定义

        函数调用函数自己,这种调用过程称为递归(recursion),递归是以自相似的方式重复项目的处理过程。函数内部调用函数自身,称为递归调用。如下:

int main()
{
	recursion();
}

void recursion()
{
	recursion(); // 函数调用自身 
}

       结束递归是使用递归的难点,因为如果递归代码中没有终止递归的条件测试部分,一个调用自己的函数会无限递归。

示例

       先看一个程序示例1,来理解递归:

#include <stdio.h>
void up_and_down(int);  //函数原型

int main(void)
{
	up_and_down(1);  //调用函数
	return 0;
}

void up_and_down(int n)
{
	printf("Level %d: n location %p\n", n, &n); // #1

	if (n < 3)
		up_and_down(n + 1);

	printf("LEVEL %d: n location %p\n", n, &n); // #2
}

       在下面程序中的 main() 函数调用 up_and_down()函数,这次调用称为“第1级递归”。然后 up_and_down()调用自己,这次调用称为“第2级递归”。接着第2级递归调用 第3级递归,以此类推。该程序示例共有3级递归,使用函数printf()函数显示了变量n的值和储存n的内存地址 &n,便于理解。

       编译和执行上面的代码:

Level 1: n location 00CFFB24
Level 2: n location 00CFFA4C
Level 3: n location 00CFF974
LEVEL 3: n location 00CFF974
LEVEL 2: n location 00CFFA4C
LEVEL 1: n location 00CFFB24

示例详解

       仔细分析程序示例1中的递归是如何工作的。

       首先,main()调用了带参数1的up_and_down()函数,执行结果是up_and_down()中的形式参数n的值是 1,所以打印语句#1打印Level 1。

Level 1: n location 00CFFB24

       然后,由于n小于3,up_and_down()(第1级)调用实际参数为n + 1(2)的up_and_down()(第2级)。于是第2级调 用中的n的值是2,打印语句#1打印Level 2。

Level 2: n location 00CFFA4C

       之后,由于n小于3,第3级调用打印Level 3。

Level 3: n location 00CFF974

       当执行到第3级时,n的值是3,if (n < 3)测试条件为假。up_and_down()函数不再调用自己。第3级调用接着执行打印语句#2,即打印LEVEL 3,

LEVEL 3: n location 00CFF974

       因为 n 的值是3。此时,第3级调用结束,控制被传回它的主调函数(即第2级调用)。在第2级调用中,执行的最后一条语句是调用if语句中的第3级调用。 被调函数(第3级调用)把控制返回在这个位置,因此,第2级调用继续执行后面的代码,打印语句#2打印LEVEL 2。

LEVEL 2: n location 00CFFA4C

       然后第2级调用结束,控制被传回第1级调用,接着打印LEVEL1,以此类推。

LEVEL 1: n location 00CFFB24

       注意:每级递归的变量 n 都属于本级递归私有。这从程序输出的地址值可以看出(当然,不同的系统表示的地址格式不同,这里关键要注意, Level 1和LEVEL1的地址相同,Level 2和LEVEL2的地址相同,等等)。

        如果觉得不好理解,可以假设有一条函数调用链——fun1()调用 fun2()、fun2()调用 fun3()、fun3()调用fun4()。当 fun4()结束时,控制传回 fun3();当fun3()结束时,控制传回 fun2();当fun2()结束时,控制传回 fun1()。递归的情况与此类似,只不过fun1()、fun2()、fun3()和fun4()都是相同 的函数。

需注意点:

        第一每级函数调用都有自己的变量。也就是说,第1级的n和第2级的n 不同,所以程序创建了3个单独的变量,每个变量名都是n,但是它们的值各不相同。当程序最终返回 up_and_down()的第1 级调用时,最初的n仍然是它 的初值1,如图所示

        第2:每次函数调用都会返回一次。当函数执行完毕后,控制权将被传回上一级递归。程序必须按顺序逐级返回递归,从某级up_and_down()返回上一级的up_and_down(),不能跳级回到main()中的第1级调用。

        第3:递归函数中位于递归调用之前的语句,均按被调函数的顺序执行。例如,程序清单1中的打印语句#1位于递归调用之前,它按照递归的顺序:第1级、第2级和第4级,被执行了3次。

        第4:递归函数中位于递归调用之后的语句,均按被调函数相反的顺序 执行。例如,打印语句#2位于递归调用之后,其执行的顺序是第4级、第3 级、第2级、第1级。

        第5:虽然每级递归都有自己的变量,但是并没有拷贝函数的代码。程序按顺序执行函数中的代码,而递归调用就相当于又从头开始执行函数的代码。除了为每次递归调用创建变量外,递归调用非常类似于一个循环语句。 实际上,递归有时可用循环来代替,循环有时也能用递归来代替。            最后:递归函数必须包含能让递归调用停止的语句。通常,递归函数都使用if或其他等价的测试条件在函数形参等于某特定值时终止递归。为此, 每次递归调用的形参都要使用不同的值。例如,程序清单1中的 up_and_down(n)调用up_and_down(n+1)。最终,实际参数等于3时,if的测试 条件(n < 3)为假。

案例

        递归函数在解决许多数学问题上起了至关重要的作用,比如计算一个数的阶乘、生成斐波那契数列,等等。

数的阶乘

        使用递归函数计算一个给定的数的阶乘:

#include <stdio.h>

int factorial(unsigned int i);  //函数原型

int main()
{
	int i = 15;
	printf("Factorial of %d is %d\n", i, factorial(i));  //在参数列表中调用factorial()函数

	return 0;
}

int factorial(unsigned int i)
{
	if (i <= 1) 
	{
		return 1;
	}

	return i * factorial(i - 1);  //依次返回并记录从15*14*13*12......*3*2的阶乘值
}

       编译和执行上面的代码:

Factorial of 15 is 2004310016

斐波那契数列

        使用递归函数生成一个给定的数的斐波那契数列(这个数列从第3项开始,每一项都等于前两项之和。定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(≥ 2,∈ N*))。

#include <stdio.h>

int fibonaci(int i);

int main()
{
	int i;
	for (i = 0; i < 10; i++)
		printf("%d\t\n", fibonaci(i));  // 水平制表符(\t)将活跃位置移至下一个水平制表点

	return 0;
}

int fibonaci(int i)
{
	/*根据定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*))*/
	if (i == 0)  //F(0)=0
		return 0;
	if (i == 1)  //F(1)=1
		return 1;

	return fibonaci(i - 1) + fibonaci(i - 2);//F(n)=F(n - 1)+F(n - 2)
}

        编译和执行上面的代码:

0
1
1
2
3
5
8
13
21
34

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值