递归与循环的互换性


author: hjjdebug
date: 2025年 06月 10日 星期二 14:28:58 CST
descrip: 递归与循环的互换性


文章目录

  • 1.程序结构的基本概念
  • 2.什么是递归?
    • 2.1 破除递归的神秘性!
    • 2.2 递归函数的执行过程.
  • 3 递归函数举例.
    • 3.1 简单的循环,怎样用递归表示
    • 3.2 连加, 循环与递归算法
    • 3.2 阶乘, 循环与递归算法
    • 3.3 费波那伽, 循环与递归算法

1.程序结构的基本概念

程序的基本结构是顺序,选择和循环, 也分别对应着顺序语句,选择语句和循环语句.
顺序语句,一条一条向下走就行.
选择语句,会判断条件,或者继续顺序执行,或者跳过一段代码后执行.
循环语句, 就是goto back; 跳转到一个旧地址重复执行.
从汇编的角度看,有了选择语句,就可以实现循环语句,
但是从上层结构看,通常把选择语句看成是if-else 语句和switch 语句.
而把循环语句用for循环和while循环来表示.
c++语言又扩展了一种迭代的描述: for(iterator it=begin,it!=end,it++); for(m : arr);
迭代方式也代表一种循环结构.

循环的核心结构是一个回跳语句go-back,才能实现代码的重复执行.

所有的循环都可以用递归来表达, 同样,所有的递归也可以用循环来表达.
递归的核心是一个call-back, 实现执行地址向低地址回跳.
就是说通常代码执行程序地址都是依次向下递长,顺序执行,一但发生调转就要注意了.
往前跳,可能是if-else 或switch 语句, 往后跳,就是循环. 一个函数内往自己的脑袋顶上call调用,就是递归.

2.什么是递归?

递归的定义大概是说在一个函数、过程或数据结构的定义中,直接或间接地调用自身的方法
因为递归牵扯到调用, 所以还是以函数递归来说明比较方便.

函数递归:
在一个函数F(i)中, 其中i是输入参数, 调用了函数F(i-1),这就是函数递归.
从汇编的角度看, 一个递归函数(是一个函数调用入口),在执行的过程中,忽然又call_back自己
的函数入口, 这就是递归函数了, 如果是go_back到某处, 那就是循环了. 循环和递归本质上就这么一点点差别,
所以可以互相替换.
call_back 有堆栈入栈, ret有堆栈出栈的操作,包括函数调用参数也可能用堆栈传送.
go_back 则没有栈操作,直接回跳.

2.1 破除递归的神秘性!

开始学习递归时,总觉得递归是规律性很强的代码, 例如费波那伽公式F(n)=F(n-1)+F(n-2);
连乘公式 n! = n * (n-)!, 连加公式 sum=1+2+3+…+n, 还有2叉数的左递归,右递归.
以为写递归必先找递推公式,
而公式这种有规律的东西太难找了. 除了我们熟悉的几个公式,你造一个或者发现一个公式出来试试?! 很难找,所以很少用递归写代码.
其实递归等价于循环, 是另一种代码复用方式, 是一种程序执行时的call_back,不需要什么寻找递推公式.
破除了这种心理障碍,写递归函数也就很平常了.

2.2 递归函数的执行过程.

我们考虑 F(i)=foo1+F(i-1)+foo2; 的递归函数,
这个函数需要抽象理解,其中foo1,foo2代表简单陈述语句. i-1可以理解为距离退出条件更近一步的意思.
整个的执行过程像一个V字型,foo1被展开执行了n遍, foo2被展开执行了n遍
foo1为V 的左边执行代码,堆栈依次下降被执行,也是先序执行或正序执行或调用执行.结果是 foo1(i), foo1(i-1),…foo1(1),foo1(0);
foo2为V 的右边执行代码,堆栈依次上升被执行,也是后序执行或倒序执行或返回执行,结果是 foo2(0), foo2(1),…foo2(i-1),foo2(i);
从它的执行过程被展开我们可以看出,递归可以用循环来代替, foo1,foo2就是执行体,但要巧妙安排.

3 递归函数举例.

3.1 简单的循环,怎样用递归表示

例: 打印1到10 10个数据,用循环和递归怎样表示
循环:

#include 
int main(void)
{
	for(int i=1;i<10;i++)
	{
		printf("i:%d\n",i);
	}
	return 0;
}

递归:

$ cat main.cpp
#include 

void F(int i)
{
	if(i==10) return;  //递归退出条件
	printf("i:%d\n",i);
	F(++i);
}
int main(void)
{
	F(1);
	return 0;
}

3.2 连加, 循环与递归算法

$ cat main.cpp
#include 

int Sum2(int i)
{
	if(i==1) return 1;  //递归退出条件
	return i+Sum2(i-1); //执行体 i+(i-1)+...1, v左侧下降
}
int Sum1(int i)
{
	int d=0;
	while(i>0)  //循环退出条件
	{
		d+=i;   //执行体 5+4+...+1
		i--;
	}
	return d;
}
int main(void)
{
	int n1=Sum1(5);
	int n2=Sum2(5);
	printf("n1:%d\n",n1);
	printf("n2:%d\n",n2);
	return 0;
}

执行结果
$ ./tt
n1:15
n2:15

3.2 阶乘, 循环与递归算法

$ cat main.cpp
#include 

int Fac2(int i)
{
	if(i==1) return 1;  //递归退出条件
	return i*Fac2(i-1); //执行体 i*(i-1)*...1, v左侧下降
}
int Fac1(int i)
{
	int d=1;
	while(i>0)  //循环退出条件
	{
		d*=i;
		i--;
	}
	return d;
}
int main(void)
{
	int n1=Fac1(5);
	int n2=Fac2(5);
	printf("n1:%d\n",n1);
	printf("n2:%d\n",n2);
	return 0;
}

执行:
$ ./tt
n1:120
n2:120

3.3 费波那伽, 循环与递归算法

这一次,反而是递归好写,循环难写,要合适的设置变量记录状态.构建循环体.

#include 
int Fib2(int i)
{
	if(i==1) return 1;  //递归退出条件
	if(i==2) return 1;  //递归退出条件
	return Fib2(i-1)+Fib2(i-2);  //执行体
}
int Fib1(int i)
{
	int a=1;  //f(1)=1
	int b=1;  //f(2)=1
	int c=1;  //c=f(1)+f(2)
	int l=2;
	while(l<i)  //循环退出条件
	{
		c=a+b;  //计算下一个f
		a=b;    //把数值向前移动
		b=c;
		l++;   //递长循环次数
	}
	return c;
}
int main(void)
{
	int n1=Fib1(8);
	int n2=Fib2(8);
	printf("n1:%d\n",n1);
	printf("n2:%d\n",n2);
	return 0;
}

执行结果:
$ ./tt
n1:21
n2:21

其它就不多举例了.
核心说明了递归和循环只是call_back 和 jump_back 的区别.
递归堆栈沿V型左边递,沿V型右边归. 循环无堆栈变化
它们都完成了重复执行某个代码块的任务. 都需要有退出条件.

你可能感兴趣的:(#,C,编程,c,递归,循环)