递归算法

递归算法

一、思维导图概要

在这里插入图片描述

二、递归算法的执行过程

在执行递归函数时会直接调用自身,但仅仅有这种操作,将会出现无休止地调用而陷入死循环。因此,一个正确的递归函数虽然每次调用的是相同的代码,但它的参数、输入数据等均有变化,并且在正常情况下随着调用的不断深入必定会出现调用到某一层的函数时不再执行递归调用而终止函数的执行,即遇到递归出口。
递归函数可以看成是一种特殊的函数,递归函数调用是函数调用的一种特殊情况,即它是调用自身代码,因此也可以把每一次递归调用理解成调用自身代码的一个复制件。由于每次调用它的参数和局部变量均不相同,所以保证了各个复制件执行时的独立性。
但递归调用在内部实现时并不是每次调用都真的复制一个函数复制件存放到内存中,而是采用代码共享的方式,也就是它们都是调用同一个函数的代码。为此系统设置了一个系统栈,为每一次调用开辟了一组存储单元,用来存放本次调用的返回地址以及被中断的函数参数值(即一个栈帧,可以理解为一个栈元素),然后将其进入系统栈(栈元素进栈),再执行被调用函数代码。当被调用函数执行完毕后,对应的栈帧被弹出(栈元素出栈),返回计算后的函数值,控制转到相应的返回地址继续执行,显然当前正在执行的调用函数的栈帧总是位于系统栈的最顶端。
所以一个函数调用过程就是将数据(包括参数和返回值)和控制信息(返回地址等)从一个函数传递到另一个函数。另外,在执行被调用函数的过程中还要为被调用函数的局部变量分配空间,在函数返回时释放这些空间,这些工作都是由系统栈完成的。
ps:在C/C++中,系统栈是从高地址向低地址延伸的。每个函数的每次调用都有它自己独立的一个栈帧,在这个栈帧中有所需要的各种信息,栈帧的大小并不固定,一般与其对应函数的局部变量的多少有关。寄存器ebp只想当前栈帧的底部(高地址),寄存器esp指向当前栈帧的顶部(低地址)。函数返回值是通过eax寄存器实现的。

归纳起来,递归调用的实现是分两步进行的,第一步是分解过程,即递归体将“大问题”分解成“小问题”,直到递归出口为止,然后进行第二步的求值过程,即已知“小问题”,计算“大问题”

在递归函数执行时,其形参会随着递归调用发生变化,但是每次调用后会恢复为调用前的形参,将递归函数的非引用型形参的取值称为状态(递归函数的引用型形参在执行后会回传给实参,有时类似全局变量,不作为状态的一部分),在调用过程中状态会发生变化,而每次调用后会自动恢复为调用之前的状态。

三、递归算法设计的一般步骤

递归算法求解过程的特征是先将整个问题划分为若干个子问题,通过分别求解子问题,最后获得整个问题的解,这些子问题具有与原问题相同的求解方法,于是可以再将它们划分为若干个子问题,分别求解,如此反复进行,知道不能再划分成子问题或者可以求解为止。
这种自上而下将问题分解,再自下而上求值、合并,求出最后问题解的过程称为递归求解过程,它是一种分而治之的算法设计方法。
在实际应用中要使用递归算法通常需要分析一下3个方面的问题:
①每一次递归调用在处理问题的规模上都应该有所缩小。
②相邻两次递归调用之间有紧密的联系,前一次要为后一次递归调用做准备,通常是前一次递归调用的输出作为后一次递归调用的输入。
③在问题规模极小时必须直接给出问题解而不再进行递归调用,因此每次递归调用都是有条件的,无条件递归调用将会称为死循环而不能正常结束。

四、递归数据结构及其算法设计

1.递归数据结构的定义
采用递归方式定义的数据结构称为递归数据结构。在递归数据结构定义中包含的递归运算称为基本递归运算

2.基于递归数据结构的递归算法设计
(1)单链表的递归算法设计
例1:有一个不带头结点的单链表L,设计一个算法释放其中的所有结点

void DestoryList(LinkNode *&L){
	if(L!=NULL){
	DestoryList(L->next);
	free(L);
	}
}

例2:有一个不带头结点的单链表L,设计一个算法删除其中所有结点值为x的结点

void Delallx(LinkNode *&L,Elemtype x){
LinkNode *p;
if(L==NULL) return;
if(L->data==x){
p=L;
L=L->next;
free(p);
Delallx(L,x);
}
else Delallx(L,x);
}

3.二叉树的递归算法设计
例1:对于含n个结点的二叉树,所有结点值为int类型,设计一个算法由其先序序列a和中序序列b创建对立的二叉链存储结构

BTNode *CreateBt(Elemtype a[],Elemtype b[],int n){
	int k;
	if(n<=0) return NULL;
	Elemtype root=a[0];
	BTNode *bt=(BTNode*)malloc(sizeof(BTNode));
	bt->data=root;
	for(k=0;k<n;k++)
	if(b[k]==root)
	break;
	bt->lchild=CreateBt(a+1,b,k);
	bt->rchild=CreateBt(a+k+1,b+k+1,n-k-1);
	return bt;
}

例2:假设二叉树采用二叉链存储结构,设计一个递归算法释放二叉树bt中的所有结点

void DesBT(BTNode *&bt){
	if(bt!=NULL){
		DesBT(bt->lchild);
		DesBT(bt->rchild);
		free(bt);
	}
}

例3:假设二叉树采用二叉链存储结构,设计一个递归算法由二叉树bt复制产生另一棵二叉树bt1

void CopyBT(BTNode *bt,BTNode *&bt1){
	if(bt==NULL){
		bt1=NULL;
	}
	else{
		bt1=(BTNode *)malloc(sizeof(BTNode));
		bt1->data=bt->data;
		CopyBT(bt->lchild,bt1->lchild);
		CopyBT(bt->rchild,bt1->rchild);
	}
}

例4:假设二叉树采用二叉链存储结构,设计一个递归算法输出从根结点到值为x的结点的路径,假设二叉树中的所有结点值不同
解析:采用求x结点的所有祖先的犯法,因为x结点加上它的所有祖先恰好构成从根节点到x结点的路径(逆向)用vector<int>向量path存放x结点或者其祖先。

bool findPath(BTNode *b,int x,vector<int> &path){
	if(b==NULL)return false;
	if(b->data==x){
		path.push_back(x);
		return true;
	} 
	else if(findPath(b->lchild,x,path)||findPath(b->rchild,x,path)){
		path.push_back(b->data);
		return true;
	}
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值