递归讲解
复习
输入a,b,c,计算m 。已知m=)
,,max(),,max(),,max(c b b a c b b a c b a +?+ 请把求三个数的最大数max(x,y,z)定义成函数和过程两种方法作此题。
递 归
为了描述问题的某一状态,必须用到它的上一状态,而描述上一状态,又必须用到它的上一状态……这种用自已来定义自己的方法,称为递归定义。例如:定义函数f(n)为:
/n*f(n -1) (n>0)
f(n)= |
\ 1(n=0)
则当n>0时,须用f(n-1)来定义f(n),用f(n-1-1)来定义f(n-1)……当n=0时,f(n)=1。
由上例我们可看出,递归定义有两个要素:
(1) 递归边界条件。也就是所描述问题的最简单情况,它本身不再使用递归的定义。
如上例,当n=0时,f(n)=1,不使用f(n-1)来定义。
(2) 递归定义:使问题向边界条件转化的规则。递归定义必须能使问题越来越简单。
如上例:f(n)由f(n-1)定义,越来越靠近f(0),也即边界条件。最简单的情况是f(0)=1。
递归算法的效率往往很低, 费时和费内存空间. 但是递归也有其长处, 它能使一个蕴含递归关系且结构复杂的程序简介精炼, 增加可读性. 特别是在难于找到从边界到解的全过程的情况下, 如果把问题推进一步使其结果仍维持原问题的关系, 则采用递归算法编程比较合适.
递归按其调用方式分为: 1. 直接递归, 递归过程P 直接自己调用自己; 2. 间接递归, 即P 包含另一过程 D, 而D 又调用P.
递归算法适用的一般场合为:
1. 数据的定义形式按递归定义.
如裴波那契数列的定义: f(n)=f(n-1)+f(n-2); f(0)=1; f(1)=2.
对应的递归程序为:
Function fib(n : integer) : integer;
Begin
if n = 0 then fib := 1 { 递归边界 }
else if n = 1 then fib := 2
else fib := fib(n-2) + fib(n-1) { 递归 }
End;
这类递归问题可转化为递推算法, 递归边界作为递推的边界条件.
2. 数据之间的关系(即数据结构)按递归定义. 如树的遍历, 图的搜索等.
3. 问题解法按递归算法实现. 例如回溯法等.
从问题的某一种可能出发, 搜索从这种情况出发所能达到的所有可能, 当这一条路走到" 尽头 "的时候, 再倒回出发点, 从另一个可能出发, 继续搜索. 这种不断" 回溯 "寻找解的方法, 称作" 回溯法 ". 例1、给定N (N>=1),用递归的方法计算1+2+3+4+…+(n-1)+n 。
分析与解答 本题是累加问题可以用递归方法求解。本题中,当前和=前一次和+当前项,而前一次和的计算方法与其相同,只是数据不同,即可利用s(n)=s(n-1)+n 来求解,另外递归调用的次数是有限次,且退出的条件是当n=1时s=1,这恰好符合递归算法的使用条件。
程序代码如下:
program p_1(input,output);
var s,t:integer;
function fac(n:integer):integer;
begin
if n=1 then fac:=1 else fac:=fac(n-1)+n;
end;
begin
read(t); s:=fac(t); writeln(‘s=’,s);
end.
0 fac(6)
1 6+fac(5)=21
2 返回
3
4
5
6 1
例2、阶乘n!=1*2*3…(n-1)*n ,可以改写成n!=(n-1)!*n ,这是阶乘用阶乘定义,但是(n-1)!是n!的简单情况。要求n!必须用同样的方法先求简单情况(n-1)!,要求(n-1)!必须用同样的方法先求简单情况(n-2)!,…最终递归到0!而0!=1。因此n!就是一个递归的描述。阶乘的递归定义:
n!=???=>-010)!
1(*n n n n
program facn(input,output);
var n:integer;y:real;
function fac(n:integer):real;
begin
if n=0 then fac:=1
else fac:=n*fac(n-1)
end;
begin
read(n); y:=fac(n); writeln(n,’!=’,y);
end.
分析程序是如何执行的?
例3 求m 与n 的最大公约数
讨论:从数学上可以知道求m 与n 的最大公约数等价于求n 与(m mod n )的最大公约数。这时可以把n 当作新的m ,(m mod n )当作新的n ,问题又变成了求新的m 与n 的最大公约数。它又等价于求新的n 与(m mod n )的最大公约数……如此继续,直到新的n=0时,其最大公约数就是新的m 。
例如求24与16的最大公约数,等价于求16与(24 mod 16)的最大公约数,即求16与8的最大公约数。它又等价于求8与(16 mod 8)的最大公约数,即求8与0的最大公约数。此时n=0,最大公约数就是8。此过程可简单地列表为
m n
24 16
16
8 8 0
其一般公式是gcd (m,n)=???>=0
)mod ,gcd(0n n m n n m
其中gcd(m,n)代表求m 与n 的最大公约数。按照此公式可以编出如下递归函数的程序。
Program gmn(input,output);
Var m,n,g:integer;
Function gcd(m,n:integer):integer;
Begin
If n=0 then gcd:=m
Else gcd:=gcd(n, m mod n)
End;
Begin
Read(m,n);g:=gcd(m,n);writeln(‘m=’,m,’n=’,n,’gcd=’,g);
End.
例4、写出下面程序运行的结果
procedure p;
begin
write(1);
end;
procedure t(j:integer);
var i:integer;
begin
for i:=1 to 2 do
begin if j=3 then p else t(j+1); end;
end;
begin
t(1); writeln;
end.
例5、相传在古印度的布拉玛婆罗门圣庙的僧侣在进行一种被称为汉诺塔的游戏,其装置是一块铜板,上面有三根杆(编号A 、B 、C ),A 杆上自下而上、由大到小按顺序串上64个金盘。游戏的目标是把 A 杆上的金盘全部移到C 杆上,并仍按原有顺序叠好。条件是每次只能移动一个盘,并且在每次移动都不允许大盘移到小盘之上。现要求
利用递归调用技术给出N 个盘从A 杆移到C 杆的移动过程。
分析:这个移动过程很复杂与烦琐,但规律性却很强。使用递归调用技术来解决这个移动过程,先得找到一个递归调用模型。想要得到汉诺塔问题的简单解法,着眼点应该是移动A 杆最底部的大盘,而不是其顶部的小盘。不考虑64个盘而考虑N 个盘的一般情况。要想将A 杆上的N 个盘移至C 杆,我们可以这
样设想:
1.以C盘为临时杆,从A杆将1至N-1号盘移至B杆。
2.将A杆中剩下的第N号盘移至C杆。
3.以A杆为临时杆,从
B杆将1至N-1号盘移至C 杆。我们看到,步骤2只需移动一次就可以完成;步骤1与3的操作则完全相同,唯一区别仅在于各杆的作用有所不同。这样,原问题被转换为与原问题相同性质的、规模小一些的新问题(下图)。即: HANOI(N,A,B,C) 可转化为 HANOI(N-1,A,C,B)与HANOI(N-1,B,A,C)
其中HANOI中的参数分别表示需移动的盘数、起始盘、临时盘与终止盘,这种转换直至转入的盘数为0为止,因为这时已无盘可移了。这就是需要找的递归调用模型。
程序清单:
program ex5_9;
var a,b,c:char; n:byte;
procedure hanoi(n:byte;a,b,c:char);
begin
if n=1 then writeln(a,’->’,c)
else
begin
hanoi(n-1,a,c,b);
writeln('Move ',a,' to ',c);
hanoi(n-1,b,a,c);
end;
end;
begin
a:='A';b:='B';c:='C';
write('N=');readln(n);
hanoi(n,a,b,c);
end.
var a,b,c,d:integer;
Procedure p(a:integer;var b:integer);
Var c:integer;
Begin
a:=a+1;b:=b+1;c:=c+1;d:=d+1;
If a<3 then p(a,b);
Writeln(a,b,c,d);
End;
Begin
a:=1;b:=1;c:=1;d:=1;p(a,b);writeln(a,b,c,d);
End.
例7、把一个十进制整数转化为K进制数。(K<=10)
分析与解答根据数制转化的规则,把一个十进制数转化为K进制数,只要用K依次去除这个数及其商,所得的余数依次为K进制数相继的低位数字。依照这个方法,直到商为0为止,最后的余数作为K进制数的最高位数字。
本题是否选用递归算法来做呢?
我们看一看它是否符合递归使用的三个条件:
1、前后数据之间存在一定的联系。显然,前一次求得的商就是后一次进一步求余的被除数,这个条件符合。
2、有限次递归调用。对一个十进制数向K进制数转化的问题,由于K是有限的,所以意味着递归调用也是有限次。
3、结束条件是:商为0则递归结束。
由此看来,本题可以使用递归算法实现。
程序代码如下:
program p_3(input,output);
var n,k:integer;
procedure tentok(n,k:integer);
var d:integer;
begin
d:=n mod k; n:=n div k; if n<>0 then tentok(n,k); write(d); end;
begin
write(‘please input n and k(-10):’);
readln(n,k);tentok(n,k);
writeln;
end.
例8、把自然数N分解为若干个自然数之积。
如输入12
输出:
12=12
12=3*4
12=2*6
12=2*2*3
total:4
参考程序:
var path:array[1..1000] of integer;total,n:integer;
procedure find(k,sum,dep:integer);
var b,d:integer;
begin
if sum=n then
begin
write(n,'=',path[1]);
for d:=2 to dep-1 do write('*',path[d]);
writeln;inc(total);
exit;
end;
if sum>n then exit;
for b:=trunc(n/sum)+1 downto k do
begin
path[dep]:=b;find(b,sum*b,dep+1);
end;
end;
begin
readln(n);total:=0;find(2,1,1);writeln('total:',total);
readln;
end.
例9、要求找出具有下列性质的数的个数(包含输入的自然数n):
先输入一个自然数n(n<=500),然后对此自然数按照如下方法进行处理:(1)不作任何处理(即自然数n本身);
(2)在它的左边加上一个自然数,但该自然数不能超过原数的一半;(3)加上数后,继续按此规则进行处理,直到不能再加自然数为止。
样例:
输入:6
满足条件的数为
6
16
26
126
36
136
输出:6
program p_lx3_1(input,output);
var n,I:integer;s:real;
procedure qiu(x:integer);
var k:integer;
begin
if x<>0 then begin
s:=s+1;
for k:=1 to x div 2 do qiu(k);
end;
end;
begin
readln(n);s:=0;qiu(n);writeln(s:2:0);
end.
Program p_lx3_2(input,output);
Var n,I:integer;s:real;a:array[1..100] of integer;
Procedure print(dep:integer);
Var I:integer;
Begin
For I:=dep-1 downto 1 do
Write(a[I]);
Writeln;
End;
Procedure qiu(x:integer;dep:integer);
Var k:integer;
Begin
Print(dep);s:=s+1;
If x<>1 then
For k:=1 to x div 2 do
Begin a[dep]:=k;qiu(k,dep+1); end;
End;
Begin
Readln(n);s:=0;a[1]:=n;qiu(n,2);writeln(s:2:0);
End.
仔细分析这二个程序的区别。
例10 2的幂次方表示
任何一个正整数都可以用2的幂次方表示.例如:137=2^7+2^3+2^0。同时约定次方用括号来表示,即a^b可表示为a(b)。由此可知,137可表示为:2(7)+2(3)+2(0),进一步:7=2^2+2+2^0 (2^1用2表示);3=2+2^0;所以最后137可表示为:2(2(2)+2+2(0))+2(2+2(0))+2(0)。又如:1315=2^10+2^8+2^5+2+1;
所以1315最后可表示:2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)
输入:正整数(n≤20000)
输出:符合约定的n的0,2表示(在表示中不能有空格)
题解
解法1:递归法
2的幂次方由高幂向低幂分解
1.计算最接近n(小于n)的2的次幂e
2e≤n<2e+1
设x=2e。
2.从e开始按照次幂递减的方向分解(0≤i≤e)
设x1—项数。若n≥x,则分解出x=2i的2幂次方表示:
⑴若x1>0(非第一项),则输出’+’;
⑵分析次幂i
i=0,输出2(0); {递归边界}
i=1,输出2;
i>1,输出2(i的2幂次方表示) {递归}
⑶准备分解下一项
x1←x1+1;n←n-x;
x←x div 2;(无论n是否大于等于n)
由此得出算法:
procedure solve(n:word);
var
e,i,x1:byte;
x:word;
begin
x←16384;e←14; {214<20000<215}
while x>n do {计算最接近n(小于n)的x=2e}
begin
x←x div 2; e←e-1
end;{while}
x1←0;
for i←e downto 0 do {逐位分解2的幂次i}
begin
if n≥x {若能分解x=2i}
then begin
if x1>0 then write(’+’); {若当前是中间项} case i of {根据次幂i分解x的2幂次方表示} 0: write(’2(0)’);
1: write(2);
else begin
write(’2(’);
solve(i);
write(’)’)
end{else}
end;{case}
x1←x1+1; n←n-x {准备分解下一项x=2i-1}
end;{then}
x←x div 2
end{for}
end;{solve}
解法2:program p_lx6(input,output);
var n:integer;
procedure bin(k:integer);
var b:array[0..15] of integer;I,p:integer;first:Boolean;
begin
p:=-1;
if k=0 then write(0)
else begin
while k>0 do
begin
inc(p);b[p]:=k mod 2;k:=k div 2;
end;
first:=true;
for I:=p downto 0 do
if b[I]=1 then
begin
if first then first:=false else write(‘+’);
if I=1 then write(‘2’)
else begin
write(‘(’);
bin(i);
write(‘)’);
end;
end;
end;
end;
begin
write(‘n=’);readln(n);bin(n);writeln;
end.
使用递归求解问题,通常可以将一个比较大的问题层层转化为一个与原问题相类似、规模较小的问题,进而最终导致原问题的解决。
例11 求数字的乘积根。一个正整数的数字的乘积N的定义是:这个整数中非零数字的乘积。例如,整数999的数字乘积为9*9*9,即729。729的数字乘积为7*2*9,即126。126的数字乘积为1*2*6,即12。12的数字乘积为1*2,即2。一个正整数的数字乘积根N是这样得到的:反复取该整数的数字乘积,直到得到一位数字为止。例如,在上面的例子中数字的乘积根是2。
编写一个程序,输入一个正整数(长度不超过200位数字),输出计算其数字乘积根的每一步结果。
分析:每一步得到的乘积根比它的上一步的位数要小,显然存在递归过程,递归结束的条件是乘积根的位数等于1。具体程序编码如下:
program ex5_17;
var st:string;
procedure init;
begin
writeln(‘please input:’);readln(st);
end;
procedure make(ss:string);
var a,b:array[1..200] of integer;I,j,x,code:integer;w:string;
begin
fillchar(a,sizeof(a),0);
for I:=1 to length(ss) do
val(ss[I],a[I],code);
fillchar(b,sizeof(b),0);
x:=1;b[1]:=1;
for I:=1 to length(ss) do
begin
for j:=1 to x do
if a[I]<>0 then b[j]:=b[j]*a[I];
for j:=1 to x do
begin
b[j+1]:=b[j+1]+b[j] div 10;
b[j]:=b[j] mod 10;
end;
if b[x+1]>0 then x:=x+1;
end;
ss:=‘’;
for I:=x downto 1 do
begin
str(b[I],w);ss:=ss+w;
end;
writeln(ss);
if length(ss)>1 then make(ss);
end;
begin
init;writeln(st);make(st);readln;
end.
例12、骨牌铺法
有1*n的一个长方形,用一个1*1、1*2、1*3的骨牌铺满方格。例如当n=3时为1*3的方格。此时用1*1,1*2,1*3的骨牌铺满方格,共有四种铺法。图4.4.3列出了四种铺法。
输入n(0<=n<=30) 输出铺法总数
题解:这道题可以采用猜测法,从具体的n=1,2,3,......开始,列举出结果,根据列举的部分结果进行猜测,推导出公式。这个猜测推导过程留给读者完成。问题是:这种方法中“猜”和“凑”的成分比较比较多,容易出错。我们不妨采用组合数学常用的待定系数进行归纳和推导。设推导公式如下:
f(n)=a*f(n-1)+b*f(n-2)+c*f(n-3)+d*f(n-4)+....(a,b,c,d...是常系数)
即1*n的长方形的铺法由全部(a种)1*(n-1)的长方形铺法总数加上全部(b种)1*(n-2)的长方形的铺法总数加上全部(c种)1*(n-3)的长方形的铺法总数......注意排除重复情况。
(1)将n格分成1格和n-1格,计算f(n-1)的系数a。右端1格的铺法有一种(图4.4.4(a))。显然,在一格中只有一种铺法,即f(n-1)的系数a=1。
(2)将n格分成2格和n-2格,计算f(n-2)的系数b。右端2格的铺法有两种(图4.4.4(b))。由图可见,(b)的铺法包含在(a)的铺法中,而(c)的铺法不同于(a),因此f(n-2)的系数b=1。
n-1格(a)
n-2格(b)
n-2格(c) 图4.4.4
(3)将n格分成3格和n-3格,计算f(n-3)的系数c。右端3格的铺法有两种(图4.4.5)。由图可见,(d)
(e)(f)的铺法都可以归结到1格或2格中去,只有1*3的铺法(g)属于新的,因此f(n-3)的系数c=1。将n格分成n-x格和x格(4<=x<=n-4)的情况都是重复的,因此不再讨论。由此得出:
n-3格(d)
n-3格(g)
f(n)=f(n-1)+f(n-2)+f(n-3) (n>=5)
var n:integer;
function f(i:integer):longint;
begin
if i in[1..2]
then f:=i
else if i=3
then f:=4
else f:=f(I-1)+f(I-2)+f(I-3);
end;
begin
readln(n); writeln(f(n));
end.
“铺砖问题”有推广价值。例如某人走n级的楼梯,每步可以走1级、2级或3级,走完n级楼梯共有多少种走法。这个问题的数学意义和解法与“铺砖问题”相同。
例13、楼梯共有N阶台阶,上楼可以一步上一个台阶,也可以一步上二个台阶。编一个程序,计算上N阶
台阶,共有多少种走法?
算法分析:根据前面的例子,我们知道递归程序执行包含递归和递推二个过程,而这二个过程又都是根据一个递推公式进行的,所以在这里我们先试着找出一个能体现不同规模问题之间关系的递推公式。
找出递推公式:我们可以试着用逆向思维来找这个递推公式,即从最终目标考虑,而不是从初始状态考(回忆一下, 在计算N!=N*(N-1)!不也是这样来的吗?)。最终目标是指到达第N 阶台阶,然后再逆向分析,到达N 阶台阶的前一步在那里?只是是在N-1阶或N-2阶上。现在我们已经得到了具有直接联系的二种状态,接下来就去找他们之间的关系式。为了表示的简洁,设上N-1阶台阶共有F (N-1)种走法,上N-2阶台阶共有F (N-2)种走法,则上N 阶台阶有F (N )种走法。假设你已到达N-1阶,现在你有多少种走法到达N 阶呢?当然只有一种(跨一步到达N 阶)。因此通过N-1阶到达N 阶的走法有F (N-1)种。再假设你已到达N-2阶,从这里到N 阶有多少种走法?有二种,一种是先到第N-1阶,再到N 阶(一步1阶走);另一种是直接跨2阶到N 阶。第一种走法实际已包含在先到N-1阶再到N 阶的走法中,所以在计算总走法时应不再考虑,否则就会重复计算。第二种走法中从N-2阶到N 阶(中途不经过N-1阶)只有一种走法(一步跨二阶直接到达),所以先到N-2阶(不经过N-1阶)到达N 阶的走法有F (N-2)种。综上所述,到达N 阶的走法有:F (N )=F (N-1)+F (N-2)。
参考程序
var s,n:integer;
function f(n:integer):integer;
begin
if n<3 then f:=n else f:=f(n-1)+f(n-2);
end;
begin
readln(n);s:=f(n);writeln(‘s=’,s);
end.
问题求解(直接给出答案就可)。
在平面上有n 条直线,且无三线共点。问这些直线有多少种不同的交点数?
在2*n 的一个长方形方格中,用一个1*2的骨牌铺满方格,例如当n=3时,为2*3方格,骨牌的铺放方案有三种(图1)。输入n =5;输出铺放总数。
例14、简单的背包问题。设有一个背包,可以放入的重量为s 。现有n 件物品,重量分别为w 1,w 2,…,w i ,…w n , w i (1<=I<=n),均为正整数。从n 件物品中挑选若干件,使得放入背包的重量之和正好为s 。
算法分析:
用knap(s,n)代表这一问题。(1)先取最后一个物品w n 放入包里,若w n =s ,正好放入包中,问题解决,输出结果(n, w n )。(2)若w n 1),那么问题转化为从剩余的n-1个物品中选取若干个,使得它们的重量和等于包里剩下的可放入重量(s-w n ),即knap(s,n)->knap(s-w n ,n-1);而选中的w n 是否有效还要看后续的问题knap(s-w n ,n-1)是否有解,无解的话说明先取的w n 不合适,就要放弃w n ,在剩余的物品中重新开始挑选,即由knap(s,n)->knap(s,n-1)。(3)若w n >s ,则不能放入包中,还得继续挑选;若还剩物品(即n>1),那么问题转化为从剩余的n-1个物品中选取若干个,使得它们的重量和等于s ,即knap(s,n)->knap(s,n-1)。
显然(2)、(3)中出现了递归定义,而(1)是递归结束的条件;但这是有解时出现的递归结束条件,然而还有可能无解,(2)、(3)中所剩物品不够的话(n<=1)问题就不能继续,这也是递归结束的条件。为了标志knap(s,n)是否有解,将knap(s,n)设置为布尔函数,true 为有解,false 无解。其伪代码为: procedure knap(s,n);
case s-w n of
=0:[knap:=true;print(n, w n )]
>0;[if n>1 then if knap(s-w n ,n-1)=true then [knap:=true;print(n,w n )]
else knap:=knap(s,n-1)
else knap:=false]
<0;[if n>1 then knap:=knap(s,n-1) else knap:=false]
end;
用pascal 语言具体实现时,将w 1,w 2,…w n 存入数组w:array[1..n] of integer 。为了使用case 语句,设计一个符号函数sng(x)来区分s-w n 的三种情况:
sng(x)=?????<->=0
101
00x x x 为了程序的易读性,源程序没有使用过于精简的方法。
Program exp1_4{简单背包问题}
Const m=10;
Var w:array[1..m] of integer;x,y,I:integer;f:Boolean;
Function sng(x:integer):integer;
Begin
Sng:=0;
If x>0 then sng:=1;
If x<0 then sng:=-1;
End;
Function knap(s,n:integer):Boolean;
Begin
Case sng(s-w[n]) of
0:begin knap:=true;writeln(‘number:’,n:4,’weight:’,w[n]:4); end;
1:begin if n>1 then if knap(s-w[n],n-1)=true {递归①} then begin
knap:=true;writeln(‘number:’,n:4,’weight:’,w[n]:4);
end else knap:=knap(s,n-1) {递归②}
else knap:=false
end; -1:if n>1 then knap:=knap(s,n-1) {递归③}else knap:=false;
end;
end;
begin
write(‘The number of object:’);
repeat readln(y) until y<=m;write(‘Total weight=’);readln(x);
write(‘Weight of each object:’);
for I:=1 to y do read(w[I]);
f:=knap(x,y);
if not(f) then writeln(‘Not answer ’);
end.
下面是两组运行结果:
The number of object:3
Total weight=7
Weight of each object:9 6 2
Not answer
The number of object:5
Total weight=10
Weight of each object:1 6 2 7 5
number:1 weight:1
number:3 weight:2
number:4 weight:7
源程序中三处有递归调用式,分别标以①②③,我们以上面这组数据来分析一下递归过程:knap(10,5)(1 6 2 7 5){先取5放入包内,问题转化为下一步}
}knap(5,4) (1 6 2 7)
递归③}knap(5,3) (1 6 2)
递归①}knap(3,2) (1 6)
递归③}knap(3,1) (1)
knap:=false
逐级返回的函数值是false;由于knap(5,4)=false;故首选的5不能用;于是knap(10,5)转向另一路,如下图所示。
主程序knap(10,5) {放弃(1 6 2 7 5)中的5,问题转化为下一步}
递归②}knap(10,4) 选(1 6 2 7)的7
递归①}knap(3,3) 选(1 6 2)
递归①}knap(1,2) 选(1 6),但6大于剩下的容量1
递归③}knap(1,1) 选(1)
writeln(1);knap:=true
对照源程序读懂了上面的递归调用过程,可以体会到递归算法中可隐含回溯,如首选5,并一路选下去,发现不行能回到出发点,重选7,再一路选下去,直到成功;还可以看到先选中的后打印;这些都说明系统为每一层的返回点、局部量都开辟了保存区,即所谓栈。
如果满足条件的答案不止一个,该程序只给出了满足条件的第一组答案,如
The number of object:5
Total weight=10
Weight of each object:1 4 2 7 5
number:1 weight:1
number:3 weight:4
number:4 weight:5
例15、输入一个非负整数,输出这个数的倒序数。如输入123,输出321。
算法分析:
procedure 数字倒序
begin
输出n的最右边的一个数字;
if 还有数字 then 将余下的“数字倒序”;
end
过程如下:
procedure reverse(n:integer);
var nr,nl:integer;
begin
nr:=n mod 10;{取n最右边的一个数字}
write(nr);
nl:=n div 10;{取n余下的数字}
if nl<>0 then reverse(nl);
end;
分析reverse(123)执行过程:每一次调用都为过程的局部变量分配存储单元,而每一次调用结束都返回上一次调用句的下一句(这和递归函数每次返回点不同)
主程序
nl=0是递归结束的条件
例16、折半查找的递归算法。
折半查找是在一列升序(或降序)的数中查找目的数。将这一列数置于a数组中,目的是寻找x,用top 指向低端,bot指向高端(top<=bot),mid指向中间,然后进行以下三种比较:(假设a数组升序)(1)若x=a[mid],则表示找到。