数据结构与算法——递归简论

本文介绍了C语言中递归的基本概念与应用,包括递归的定义、基准情形与不断推进原则,并通过具体示例说明了如何正确使用递归进行问题求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 本文简述了基于C语言的递归(recursive)和使用递归的四条基本法则

我们先用数学语言来描述一下什么是递归,如:
F(X)=

{0,2F(X1)+x2, X=0 X0

当一个函数使用它自己来定义时就称为是递归。在C中,函数F(X)的实现如下:
int F( int X ){
    if( X == 0 )
        return 0;
    else return 2*F(X-1)+X*X;
}

实际上,递归调用在处理上与其他的调用没什么不同。如果以F(4) 调用函数F(int x),那么程序就会计算2F(3)+44,紧接着调用F(3)……此时,F(0)必须被赋值,否则,程序将会不断地执行下去,直至崩溃。我们把F(0)=0的情况叫做基准情形(base case)。我们再来看一个错误使用递归的例子:

int Bad( unsigned int N){
    if (N == 0)
        return 0;
    else return Bad(N/3+1)+N-1;
}

我们可以看到,除0之外,对于任意的N,程序都不可能算出结果。因此,我们可以得出递归的两个基本准则:

  1. 基准情形。设计递归时总要有某些基准的情形,它们不用递归就可以求解,如上面的N==0的情况。
  2. 不断推进(making progress)。既然有了基准情形,那么对于那些需要递归求解的情形,递归调用必须总能够朝着基准情形的方向推进。

下面我们再来看看使用递归打印一个正整数N的例子(假设现在的I/O只能处理单个数字并将其输出到终端,Printout(N)为处理单个数字的输出函数),例如,PrintDigit(4)就是将“4”输出到终端。现在,我们需要实现将“12345”输出到终端,首先需要打印出‘1’,然后是‘2’……假设我们已经打印出了”1234”,再打印’5’时,使用语句PrintDigit(N%10)就可以完成。对于前面的情况,我们可以用同样的方法解决。因此,我们可以使用语句PrintOut(N/10)递归地解决这个问题。

? - 也许这里会产生疑问,“上面所说的基准情形如何定义?”。如果0N<10,我们就使用PrintDigit(N)直接输出N,所以PrintDigit(N)就是基准情形。而对于一个正整数N,我们可以通过PrintOut(N)来用较小的正整数定义它,这样也保证了递归的不断推进。

过程代码如下:

void PrintOut( unsigned int N ){
    if(N >= 10)
        printOut( N/10 );
    PrintDigit( N%10 );
}
  • 证明(前方高能)

首先,如果N只有一位数字,那么程序显然是正确的,因为它只需调用一次PrintDigit(N)
然后,设PrintOut(N)对所有k位或者位数更少的数都有效。对于k+1位的数,可以通过前k位数字和最后一位数字来表示。前k位数字就是程序中的(int)(N/10),即N/10后向下取整,而最后一位数字是Nmod10N%10)。因此,该程序能够正确地打印出任意k+1位数。于是,根据归纳法,所有的数都能被正确地打印出来。

因此,我们可以得出递归的第三条法则:
       3. 设计法则(design rule)。假设所有的递归调用都能运行(这也是上面为什么要证明的原因)。当设计递归程序时一般没有必要知道簿记管理的细节,因为有时追踪实际的递归调用序列是非常困难的。另一方面,这也体现了递归的好处——计算机能够算出复杂的细节。
递归的主要问题是隐含的簿记开销,虽然这些开销几乎总是合理的(既简化了算法设计,又给出了更加简介的代码),但要注意的是,不要尝试用递归来代替简单的for循环。
最后,递归的第四条法则是:
        4. 合成效益法则(compound interest rule)。在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作。


Reference:
[1]. Data Structures and Algorithm Analysis in C Second Edition, Mark Allen Weiss

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值