回 帖 发 新 帖 刷新版面

主题:递归算法及其应用

[递归的描述]
  由上面的例子可以看出,一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。因此,在考虑使用递归算法编写程序时,应满足两点:1)该问题能够被递归形式描述;2)存在递归结束的边界条件。
  递归的能力在于用有限的语句来定义对象的无限集合。用递归思想写出的程序往往十分简洁易懂。

[例2] 给出一棵二叉树的中序与后序排列。求出它的先序排列。
[分析] 通过对比二叉树的中序与后序排列,我们可以找出根节点及左右子树。同样的,有可以通过对比左子树的中序与后序排列,找出左子树的根节点……可见,该问题能够被递归描述。当找到最后一个根节点时,递归无法再进行下去,这就是递归结束的边界条件。由此可见,递归算法中常常隐含了分治思想。程序如下:
program chu01_3;
 var z,h: string;
 procedure find(a,b:string);
  var
   s,l : integer;
  begin
   l:=length(b);
   if l=1 then Write(b) {边界条件及递归返回段}
   else
    begin {递归前进段}
     Write(b[l]);
     s:=pos(b[l],a);
     if s-1>0 then find(copy(a,1,s-1),copy(b,1,s-1)); {递归左子树}
     if l-s>0 then find(copy(a,s+1,l-s),copy(b,s,l-s)); {递归右子树}
    end;
 end;
begin
 Readln(z);
 Readln(h);
 Find(z,h);
 Readln;
end.
[递归的应用]

1.经典递归
  例如hanoi塔问题:经典的递归,原问题包含子问题。有些问题或者数据结构本来就是递归描述的,用递归做很自然。

2.递归与递推
  利用递归的思想建立递推关系,如由兔子生崽而来的fibonacci数列。但递推由于没有返回段,因此更为简单,有时可以直接用循环实现。

3.分治
  不少分治方法是源于递归思想,或是递归分解+合并处理。

4.回溯
  规模较小的问题用回溯解决比较自然。注意递归前后要保证现场的保存和恢复,即正确的转化问题。

5.动态规划
  动态规划的子问题重叠性质与递归有某种相似之处。递归+动态修改查表是一种不错的建立动态规划模型的方法。

6.其他
  其他么,就是不好归类。例如表达式处理,排列组合等。附带说一下,用递归来处理打印方案的问题还是很方便的。

[例3] 求把一个整数n无序划分成k份互不相同的正整数之和的方法总数。
[分析] 这是一道动态规划题,动态方程如下:
       f[i-1,j]+f[i,j-i]+1 ((j mod i=0) and (j div i=1))
  f[i,j]:= f[i-1,j] (i>=j)
       f[i-1,j]+f[i,j-i] (else)
  s:=f(k,n-k)
本题可以用循环来实现递推,也可以考虑用递归求解。主过程如下:

方案一:
Procedure work(I,j:longint; var s:longint);
 Var t:longint;
 Begin
If (i=1) or (j=1) then s:=1
 Else if (i=0) or (j=0) then s:=0
   Else begin
       if (j mod i=0) and (j div i=1) then
            begin
             work(i-1,j,s);
             t:=s;
             work(i,j-1,s);
             s:=s+t+1;
            end
            else if (i>=j) then
                      work(i-1,j)
              else begin
                  work(i-1,j,s);
                  t:=s;
                  work(I,j-1,s);
                  s:=s+t;
                 end;
 End;

方案二:procedure search(v,w,last:byte);
var i:byte;
begin
 if w=0 then inc(count)
 else
  if w=1 then
   if v>=last then search(0,0,0) else
  else for i:=last to v-1 do search(v-i,w-1,i);
end;

  可以看出,方案一的程序较为冗长,消耗栈空间较大;而方案二较为简洁明了,所用的栈空间也较小,效率较高。因此,使用递归算法也有一个优化问题。算法的简洁与否直接制约了程序的可行性和效率。

[总结]
  递归使一些复杂的问题处理起来简单明了,尤其在学习算法设计、数据结构时更能体会到这一点。但是,递归在每一次执行时都要为局部变量、返回地址分配栈空间,这就降低了运行效率,也限制了递归的深度。因此,在必要的时候可以只使用递归的思想来求解,而程序则转用非递归的方式书写。 

回复列表 (共11个回复)

沙发

在下对您的敬佩  犹如滔滔江水,延绵不绝;黄河泛滥,一发不可收拾
但您
能不能。加点注释。

板凳

在下也对您的敬佩  犹如滔滔江水,延绵不绝;黄河泛滥,一发不可收拾
但是
    抄那么多不累吗?

3 楼


[fly]能不能找些c的来写呀?[/fly]

4 楼

如果你们不太清楚蓝色梦幻的例子(二叉树)
我举个例
如(好理解的):
  前序序列 为: a b c d e f g ;
  中序为    e g c a b d f;
a必为根结点, 然后从中序的特点知: e g c 都为a的左系子孙.b d f 为右系子孙.
再对前序继续察看,知b无左系子孙,右系为d f. 再对前序继续察看,知c无右系子孙,左系为 e g...就这样"爹妈老子"就全分清了.从中你就会看到递归的思想.

(中,后)想一想;根再最后,所以从中序,左右子树可知;再想按左到右遍历的话到数第二个(后序的)该是右子树的根(有右子树的话,从中序可知有无)..."爹妈老子"全分清了,从中你就会看到递归的思想.

(前,后)从后序知 a 是根,那末后序中a以前的部分和前序中a以后的部分的交集就是a 的子树按此方法(缩小子树)看后序得到数第二个,它所在的后序序例的前边部分和它所在的前序序例的后边部分的交集就是它的子树(没有则唯一叶子节点)...爹妈老子全出来了( 不唯一).从中你就会看到递归的思想.
就这样吧,编码你们自己完成.(前序 a b d e c f g ;后序 :d e b f g c a)
    a
  b    c
d  e  f  g
另外有一点:
    a                a
       c          c
     f   g      f   g  
    
    (1)            (2)

前序 : a c f g
后序 : f g c a
中序的作用分出左右子树.没中序不唯一(单边时).

这是我以前的思考,有不对的还请纠正.
如果你们早就明白了,当我再放屁好了!!!别骂我哦?

5 楼

To:心雨
很抱歉,c我只是看过几眼
这方面的东西不多
以后找到了贴出来
end

6 楼

可以把阳辉三角的例子说一便吗?

7 楼

可以用c递归实现的
不过我最近没时间
哪位帮忙贴一下?

8 楼

杨辉三角型的C语言实现:
# include "stdio.h"

void main()
{
int i,j;
int num[11][11];
num[1][1]=1;
num[2][1]=1;
num[2][2]=1;

for (i=3;i<=10;i++)
{
num[i][1]=1;
for (j=2;j<i;j++)
num[i][j]=num[i-1][j]+num[i-1][j-1];
num[i][j]=1;
}

for (i=1;i<=10;i++)
{
for (j=1;j<=i;j++)
printf("%d\t",num[i][j]);
printf("\n");
}
}

9 楼

其中的num[]的大小可以根据需要求的三角形的大小而变啊

10 楼

有一个2^N*2^N方格的。其中有一个放格已填充。一次一次用“L”形的三个方格去填充,要正好填充完。要用递归法

我来回复

您尚未登录,请登录后再回复。点此登录或注册