十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
循环与递归的本质区别在于内存的使用上,递归是方法调用方法本身,而随着递归的次数的增加,内存的消耗也是不断增长,而在我们写代码时,内存是一个很重要的部分,我们尽量都是减少内存的消耗,以免造成对系统资源的浪费,循环占用的内存很少,每次循环都会释放之前分配的内存,但是很多递归的功能是不能用循环实现的,这就要考虑你要实现的功能了,如果非递归不可完成的功能,我们也不会刻意更改。
创新互联公司从2013年成立,公司自成立以来始终致力于为企业提供官网建设、移动互联网业务开发(微信平台小程序开发、手机网站建设、重庆APP软件开发等),并且包含互联网基础服务(域名、主机服务、企业邮箱、网络营销等)应用服务;以先进完善的建站体系及不断开拓创新的精神理念,帮助企业客户实现互联网业务,严格把控项目进度与质量监控加上过硬的技术实力获得客户的一致赞誉。
int count = 0;
void a( void )
{
�0�2 count++;
�0�2 if ( count 100 )
�0�2 a(); // 如果count100, 调用自己。一直到100就停止!不过递归多了耗尽堆栈会崩溃!
}
递归算法:是一种直接或者间接地调用自身的算法。在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解。
递归算法的特点
递归过程一般通过函数或子过程来实现。
递归算法:在函数或子过程的内部,直接或者间接地调用自己的算法。
递归算法的实质:是把问题转化为规模缩小了的同类问题的子问题。然后递归调用函数(或过程)来表示问题的解。
递归算法解决问题的特点:
(1) 递归就是在过程或函数里调用自身。
(2) 在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。
(3) 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
(4) 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序。
递归算法所体现的“重复”一般有三个要求:
一是每次调用在规模上都有所缩小(通常是减半);
二是相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入);
三是在问题的规模极小时必须用直接给出解答而不再进行递归调用,因而每次递归调用都是有条件的(以规模未达到直接解答的大小为条件),无条件递归调用将会成为死循环而不能正常结束。
例子如下:
描述:把一个整数按n(2=n=20)进制表示出来,并保存在给定字符串中。比如121用二进制表示得到结果为:“1111001”。
参数说明:s: 保存转换后得到的结果。
n: 待转换的整数。
b: n进制(2=n=20)
void
numbconv(char *s, int n, int b)
{
int len;
if(n == 0) {
strcpy(s, "");
return;
}
/* figure out first n-1 digits */
numbconv(s, n/b, b);
/* add last digit */
len = strlen(s);
s[len] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[n%b];
s[len+1] = '\0';
}
void
main(void)
{
char s[20];
int i, base;
FILE *fin, *fout;
fin = fopen("palsquare.in", "r");
fout = fopen("palsquare.out", "w");
assert(fin != NULL fout != NULL);
fscanf(fin, "%d", base);
/*PLS set START and END*/
for(i=START; i = END; i++) {
numbconv(s, i*i, base);
fprintf(fout, "%s\n", s);
}
exit(0);
}
递归算法简析(PASCAL语言)
递归是计算机科学的一个重要概念,递归的方法是程序设计中有效的方法,采用递归编写
程序能是程序变得简洁和清晰.
一 递归的概念
1.概念
一个过程(或函数)直接或间接调用自己本身,这种过程(或函数)叫递归过程(或函数).
如:
procedure a;
begin
.
.
.
a;
.
.
.
end;
这种方式是直接调用.
又如:
procedure b; procedure c;
begin begin�0�2
. .
. .
. .
c; b;
. .
. .
. .
end; end;
这种方式是间接调用.
例1计算n!可用递归公式如下:
1 当 n=0 时�0�2
fac(n)={n*fac(n-1) 当n0时
可编写程序如下:
program fac2;
var
n:integer;
function fac(n:integer):real;
begin
if n=0 then fac:=1 else fac:=n*fac(n-1)
end;
begin
write('n=');readln(n);
writeln('fac(',n,')=',fac(n):6:0);
end.
例2 楼梯有n阶台阶,上楼可以一步上1阶,也可以一步上2阶,编一程序计算共有多少种不同的走法.
设n阶台阶的走法数为f(n)
显然有
1 n=1�0�2
f(n)={
f(n-1)+f(n-2) n2
可编程序如下:
program louti;
var n:integer;
function f(x:integer):integer;
begin
if x=1 then f:=1 else
if x=2 then f:=2 else f:=f(x-1)+f(x-2);
end;
begin
write('n=');read(n);
writeln('f(',n,')=',f(n))
end.
二,如何设计递归算法
1.确定递归公式
2.确定边界(终了)条件
三,典型例题
例3 梵塔问题
如图:已知有三根针分别用1,2,3表示,在一号针中从小放n个盘子,现要求把所有的盘子
从1针全部移到3针,移动规则是:使用2针作为过度针,每次只移动一块盘子,且每根针上
不能出现大盘压小盘.找出移动次数最小的方案.
程序如下:
program fanta;
var
n:integer;
procedure move(n,a,b,c:integer);
begin
if n=1 then writeln(a,'---',c)
else begin
move(n-1,a,c,b);
writeln(a,'---',c);
move(n-1,b,a,c);
end;
end;
begin
write('Enter n=');
read(n);
move(n,1,2,3);
end.
例4 快速排序
快速排序的思想是:先从数据序列中选一个元素,并将序列中所有比该元素小的元素都放到它的右边或左边,再对左右两边分别用同样的方法处之直到每一个待处理的序列的长度为1, 处理结束.
程序如下:
program kspv;
const n=7;
type
arr=array[1..n] of integer;
var
a:arr;
i:integer;
procedure quicksort(var b:arr; s,t:integer);
var i,j,x,t1:integer;
begin
i:=s;j:=t;x:=b;
repeat
while (b[j]=x) and (ji) do j:=j-1;
if ji then begin t1:=b; b:=b[j];b[j]:=t1;end;
while (b=x) and (ij) do i:=i+1;
if ij then begin t1:=b[j];b[j]:=b;b:=t1; end
until i=j;
b:=x;
i:=i+1;j:=j-1;
if sj then quicksort(b,s,j);
if it then quicksort(b,i,t);
end;
begin
write('input data:');
for i:=1 to n do read(a);
writeln;
quicksort(a,1,n);
write('output data:');
for i:=1 to n do write(a:6);
writeln;
end.
#includestdio.h
int account_next(int a[][8], int m, int n)
{
// 列索引n执行+1,即进入下一列
if (-1 = n n != 8)
n++;
// 当列索引n至最后一列时(n=8),行索引m执行+1,即进入下一行
else if (-1 = m m != 8)
{
n = 0;
m++;
}
// 当行索引=8时,说明已经遍历全部元素
else
return 0;
if (0 = m m 8 0 = n n 8 a[m][n] == 0)
{
// 计数a[m][n]左、右、上、下、左上、左下、右上、右下1的个数
int c = 0;
// left
if (0 n 1 == a[m][n - 1]) c++;
// right
if (7 n 1 == a[m][n + 1]) c++;
// up
if (0 m 1 == a[m - 1][n]) c++;
// down
if (7 m 1 == a[m + 1][n]) c++;
// left up
if (0 m 0 n 1 == a[m - 1][n - 1]) c++;
// left down
if (7 m 0 n 1 == a[m + 1][n - 1]) c++;
// right up
if (0 m 7 n 1 == a[m - 1][n + 1]) c++;
// right down
if (7 m 7 n 1 == a[m + 1][n + 1]) c++;
printf("a[ %d ][ %d ] 周围有 %d 个1.\n", m, n, c);
}
// 计数a[m][n]下一个元素
account_next(a, m, n);
}
int main(void)
{
int a[8][8] = {
{ 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 0, 0, 1, 0, 0, 1 },
{ 1, 0, 0, 1, 0, 0, 1, 1 },
{ 1, 1, 1, 0, 0, 1, 0, 1 },
{ 1, 0, 1, 1, 1, 0, 0, 1 },
{ 1, 1, 0, 0, 0, 1, 1, 1 },
{ 1, 1, 1, 1, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1 } };
account_next(a, 0, -1);
return 0;
}
递归是函数体中调用自己,如果不加控制,将无休止的调用自己,直到堆栈溢出。循环是反复执行某一段区域内的代码,如果不加控制,就会形成死循环。所以不管是递归还是循环,都要设定一定的条件,以结束递归或循环。
实际问题中,有一些问题是递归的,这样的问题使用递归程序解决感觉会自然些,程序也会简单些,但是,递归要经常调用函数,开销(内存、时间)大,有些问题就不适宜使用,循环不需要调用自身,甚至可以不调用函数,效率高,不过,要将递归问题改成非递归,可能就要动动脑筋。
上例中pow2
函数实现部分指数计算功能,(b,
n-1)
=3
这个提法有问题,因为递归调用时,在返回之前系统堆栈上有一大堆(从第一次调用知道条件满足时的次数)的该递归函数,条件满足后这一系列的函数依次返回。上述函数运行过程是这样的:
执行主函数的
pow2(3,
2);
后:
1:
b
=
3
n
=
2
此时
n
0;
pow2
调用自身(即递归调用):
pow2(b,
n-1)
*
b
后:
2:
b
=
3
n
=
1
此时
n
0;
pow2
调用自身(即递归调用):
pow2(b,
n-1)
*
b
后:
3:
b
=
3
n
=
此时
n
=
0,
if
(n
=
0)
条件满足
1;
递归函数第一次(函数最后依次递归调用)返回,值为
1
4:
上一次
pow2(b,
n-1)
返回值为
1,return
pow2(b,
n-1)
*
b;
所以本次(第2次)返回
3
5:
上一次
pow2(b,
n-1)
返回值为
3,return
pow2(b,
n-1)
*
b;
所以本次(第1次)返回
9
6:
函数main得到
pow2
的返回值
9