3.为什么能够递归调用?
子程序执行中,调用参数、返回地址、局部变量都在栈中分配或存放的,每一次调用未结束前,这些重要参数都在栈中得到保护,栈单元先进后出、后进先出,多级子程序互不干扰,当然要求栈的单元足够多。都是基于栈的原理,所以,递归调用与非递归调用都是一样可以实现的,每向下调用一次,栈就增加一些单元,都是子程序调用原理,没有大的区别,递归只是调用了自已而已,你要理解成一回事。
4.递归调用有什么特殊要求?
由于递归是自已调用自已,这样就会无限调用下去,直到栈单元不够用而溢出,就会引起程序崩溃。所以递归子程序中,必须设置结束条件,一旦到达某种特殊情况,子程序就不再是递归的,以便有返回到上级调用者的机会。
5.为什么需要递归?递归调用有什么优点与缺点?
对递归算法的理解可以不需要了解递归的具体执行原理,这就是“透明性质”。
例如:最简单的阶乘 n!=n*(n-1)!, 1!=1。
有此算法的描述本身就是递归的,如果用递归程序实用递归算法,就会很简单。
如:斐波那契数列,F0=0,F1=1,F(n)=F(n-1)+F(n-2)(n≥2)就是递归描述,特殊情况F(0),F(1) 是不需要递归的,就是递归的结束条件。
再比“汉诺塔”,就是经典的递归算法,只有几行程序,百度一下,了解它的递归算法原理。
Hanoi(int n,char a,char b, char c)
{
if (n==1)
显示(n,a,c);
else
{
hanoi(n-1,a,c,b);
显示(n-1,a,c);
hanoi(n-1,b,a,c);
}
}
递归的优点是:许多算法本身就是递归的,用递归实现非常简单,改成非递归就很复杂。
递归的缺点:多级递归,占用了大量的栈单元,许多是重复递归,效率很低。
6.递归的种类?
1.单级递归:
递归算法中,只出现一次调用自已,就是单级递归。
这是最简单的递归,如阶乘。n!=n*(n-1)!,1!=1
算法中,虽出现一次递归调用,但真正实现计算时,递归了n-1次,
单级递归完全可以改成循环迭代实现。
2.二级递归:
递归算法中,出现两次调用自已,就是二级递归。
斐波那契数列、汉诺塔、二叉树的遍历就是二级递归;实际递归执行时将
产生2的n-1次方递归调用,消耗的栈单元较多。
3.多级递归:
递归算法中,出现两次以上调用自已,就是多级递归,递归次数呈指数级关系,
将消耗大量的栈单元,只有练习意义,没有实用价值,行列式的递归计算就是典型例子。
7.递归的具体执行原理?
递归算法能够正确执行,但递归是如何执行的,常令初学者关切。
实际它跟普通子程序调用并没有大的区别,遵守子程序的“返回原则”而已,
你只要把递归调用理解成调用另一个同名的子程序一样即可。
int p(int n) // 求阶乘递归子程序
{
if (n==1)
return(1);
else
return(n*p(n-1));
}
如果你在主程序调用了 P(4),当它执行到 4*P(3)时,等待P(3)执行完后才能返回;
执行p(3)时,当它执行3*p(2),等待P(2)执行完后才能返回;
执行p(2)时,当它执行2*p(1),等待P(1)执行完后才能返回;
执行p(1),直接得到了结果1,P(1)执行结束,
按照返回原则,P(1)将返回到它的上一级调用者P(2),2*P(1)得到结果2,P(2)执行结束;
遵照返回原则,P(2)将返回到它的上一级调用者P(3),3*P(2)得到结果6,P(3)执行结束;
遵照返回原则,P(3)将返回到它的上一级调用者P(4),4*P(3)得到结果24,P(4)执行结束;
遵照返回原则,P(4)将返回到它的上一级调用者主程序, 执行结束;
当不能得到直接结果就会再次调用,在栈中保存局部变量及返回地址,栈越来越深,
当不再递归时,就会按栈中的返回地址,返回到它的上一级调用者继续执行,
直到返回到主程序,就象倒着执行一样。
8.稍复杂递归算法:行列式值的计算
n阶行列式值的计算可以化为n个n-1阶行列式的和,可以按第1列(y=0)展开,这就是行列式递归求值算
法的原理。
存放行列式用二维数组较好,但一般语言都不支持动态阶数,所以,就用一维数组顺序存放行列式数据,再用二维的宏表达式detV(d,x,y)模仿二维数组d[x,y],可理解性稍差点,但用一维数据可以方便余子式的产生,只要一次new就可创建单元,而指针数组要n+1次new就太多了。
行列式类TDet,就含一个阶数与一维数组地址,并封装了单元申请用释放,你可以改成不用class类。
detCopy(x,y,d,d1); 就是复制行列式 x行y列的余子式数据,余子式用了顺序存放*p++,简单化了。
递归子程序detCal(det1) 很短而简单,用了一重循环,在第一列(y=0)展开,v累计结果,余子式是有符号变化的,称代数余子式,首行正号、下一行负号,交替变化,而余子式的计算就是再次递归调用而已。
二级或一级行列式,就是递归的结束条件,二阶就直接计算了。
/-------------------------------------------
// 递归法求行列式的值 detCal
// 杨忆鸿 2015.11.11
//-------------------------------------------
#define detV(d,x,y) (d->det[(x)*(d->nn)+(y)]) // 一维d[]映射成二维d[x,y]
class TDet // 定义行列式类
{
public:
double *det; // 一维数组作为行列式,节省单元,也方便余子式的产生
int nn; // 行列式为nn X nn个单元: nn>=0
TDet(int n) // 行列式单元申请
{
nn=n;
det=new double[nn*nn];
}
~TDet()
{
delete [] det; // 释放行列式单元
}
};
void detCopy(int x,int y,TDet *d,TDet *d1)
{ // 将行列式d的x行y列的余子式复制到另一行列式d1,(nn-1)单元
double *p=d1->det;
for (int i=0;i<d->nn;i++)
for (int j=0;j<d->nn;j++)
if (x!=i && y!=j)
*p++=detV(d,i,j); // 顺序存放到d1中
}
double detCal(TDet *d) // 递归法求行列式的值
{
if (d->nn<=1) // 一阶行列式
return(detV(d,0,0));
else
if (d->nn==2) // 二阶行列式
return(detV(d,0,0)*detV(d,1,1)-
detV(d,0,1)*detV(d,1,0));
else
{ // 按第0列展开行列式
double v=0.0; // 累计行列式值
TDet *d1=new TDet(d->nn-1); // 就一次创建余子式d1单元
for (int i=0;i<d->nn;i++) // 行列式按第一列展开
{
detCopy(i,0,d,d1); // i行0列产生余子式
double t=detV(d,i,0);
if (i%2!=0)) // i奇数则余子式符号反向
t=-t;
v=v+t*detCal(d1); // 再次递归求余子式d1
}
delete d1; // 释放余子式d1单元
return(v);
}
}
// 以下是C++BUILDER主程序
String disp(double d)
{
char buf[20];
sprintf(buf,"%5.1lf",d);
return(String(buf));
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{ //
double d2[2][2]={{1,2},{3,4}};
double d3[3][3]={{1,2,3},{6,5,4},{9,8,1}};
double d4[4][4]={{4,3,2,1},{6,8,7,5},{12,11,10,9},{7,9,4,1}};
TDet *d=new TDet(3); // 数组送进行列式
for (int x=0;x<d->nn;x++)
for (int y=0;y<d->nn;y++)
detV(d,x,y)=d3[x][y];
double t=detCal(d);
delete d;
Memo1->Lines->Add(disp(t)); // 显示值
}
//
------------------------
本人原创,引用须明出处及作者
联系客服