数据结构_递归

递归

什么是递归

函数中存在着调用函数本身的情况。

递:将问题拆分为子问题解决,子问题再拆分子子问题……直到被拆解的问题无需再拆分为更细的子问题(可以开始往回求解)。

归:最小的子问题解决了,上一层的子问题解决了,上上一层子问题也就解决了……直到所有问题都被解决。

参考链接:https://www.cxyxiaowu.com/7259.html

递归算法通用解决思路

递归特点:

1、一个问题可以分解成具有相同解决思路的子问题,子子问题,也就是这些问题都能调用同一个函数,一般为函数自身

2、经过层层分解的子问题最后一定是有一个不能再分解的固定值(终止条件)向上进行返回

判断题目是否满足上述条件,使用递归?

递归解题套路:

1、定义一个函数,明确函数功能(递归的特点是问题和子问题都会调用函数自身)寻找问题与子问题之间的递归关系即可

2、寻找问题与子问题之间的关系(递推关系)由于问题与子问题具有相同的解决思路,子问题调用步骤1定义好的函数,自己调用自己,寻找最终不可再分解的子

问题的解【终止条件】确保子问题不会无限分解下去。

3、递推公式用代码表示出来,根据问题和子问题之间的关系,推导时间复杂度

注:树、二叉树题目通常使用递归解决,下面给出几个例子,自行纸上推导公式,递归结束条件

例题

1、反转二叉树/交换二叉树左右结点

分析:反转可以理解为交换二叉树的左右结点

​ 第一步,定义一个函数,代表翻转以root为根的二叉树

​ 第二步,查找问题与子问题之间的关系,得出递推公式,采用自上而下的思考方式,画图分析!

​ 递推关系:翻转(根结点)= 翻转(根结点的左结点)+ 翻转(根结点右结点)

​ InvertTree(root)=InvertTree(root->left)+InvertTree(root->right)

​ 第三步:递归终止的条件是当结点为叶子结点时终止【叶子结点没有左右结点】

TreeNode InvertTree(TreeNode root){		
    if(root==NULL)		//遍历到叶子结点时,返回空
        return NULL;
    TreeNode temp;
    //翻转左或者右结点下的左右结点
    TreeNode left=InvertTree(root->left);
    TreeNode right=InvertTree(root->right);
    
    //对于根结点的左右结点进行交换
    temp=root->left;
    root->left=root->right;
    root->right=temp;
    
    return root;
}

2、二叉树高度

分析:函数功能,求以T为根的二叉树的高度;

​ 递推关系,根结点高度=左边子树/右边子树的最大高度+1(根结点自身所在一层);

​ 终止条件,叶子结点没有左右结点就终止,叶结点所在高度为1,但是左右孩子为空就返回高度为0

int TreeDepth(BiTree T){
    if(T==NULL)
        return 0;
    LeftDepth=TreeDepth(T->lchild);
    RightDepth=TreeDepth(T->rchild);
    
    if(LeftDepth>RightDepth)
        return LeftDepth+1;
    else
        return RightDepth+1;
}

3、二叉树宽度

分析:函数功能,求每一层最大的结点数【宽度】

​ 递推关系,树的最大宽度【每一层计算结点数,存放在数组中,level为下标】

​ 终止条件,叶子结点为空就返回空。

int count[100] = {0};   	//全局数组
int max = 0;            	//宽度
Tree_width(pTree,count,1,max);	//递归求树的最大宽度,初始树level为1,向下遍历每层+1

void Tree_Width(BiTree T,int level,int *count,int &max){
    BiTree p=T;
    if(p==NULL)
        return ;
    
    count[level]++;			//count数组记录每一层结点数
    if(max<count[level])	//max保存最大层结点
        max=count[level];
    
    Tree_Width(p->lchild,level+1,count,max);		//每遍历一层,结点层次+1
    Tree_Width(p->rchild,level+1,count,max);		
    
}

4、二叉树双分支结点的个数

分析:函数功能,求某个结点是否为双分支结点=同时存在左孩子和右孩子

​ 递推关系,根结点=左孩子+右孩子,左孩子=左左孩子+左右孩子……【单分支结点=存在左孩子/右孩子】

​ 终止条件,遍历到叶子结点,返回0,没有左右孩子

例如:最左下的双分支,叶子结点都返回0,但自身是1个双分支结点

​ 单分支结点不用自身+1,只要判断孩子结点中有无双分支

int Count_DNode(BiTree T){
    BiTree p=T;
    if(p=NULL)		//叶子结点开始返回
        return 0;
    else if(p->lchild!=NULL&&p->rchild!=NULL)	//双分支结点
        return	Count_DNode(p->lchild)+Count_DNode(p->rchild)+1;
    else			//单分支结点
        return Count_DNode(p->lchild)+Count_DNode(p->rchild);
}

5、先序遍历第k个结点的值

分析:函数功能,每遍历一个结点进行计数直到第k个为止。终止条件,如果找到第k个结点的值就输出,如果找到叶子结点就返回NULL。

​ 递归条件,左子树中没有找到的判断条件是遍历到左子树中最右下结点,然后在右子树中寻找

​ 每一次递归表示遍历下一个结点,计数器加1 也就是之前都不成立,继续向后

int Search_kNode(BiTree T,int k){
    BiTree p=T;
    static int i=0;			//对静态变量i进行计数,不需要随递归次数变化
    if(p==NULL)				//叶子结点返回-1
        return -1;
    if(i==k)
        return p->data;
    i++;
    ch=Search_kNode(p->lchild,k);
    if(ch!=-1)		//左子树存在就输出,否则就右子树中找
        return ch;
    ch=Search_kNode(p->rchild,k);
}

6、删除以x为根的子树

分析:函数功能,删除以x为根的所有子树

​ 递归条件,左右子树中都有以x为根的子树,如果左子树中有释放该结点,同理右子树

​ 终止条件,叶子结点都不是就向上返回空

void Del_X(BiTree T,int x){
    BiTree p=T;
    if(p==NULL)
        return ;
    if(p->data==x)
        free(p);
    Del_X(p->lchild,x);
    Del_X(p->rchild,x);
}

7、查找x的所有祖先,假设x唯一

分析:函数功能,查找值为x的所有祖先,也就是x的父结点,x的父结点的父结点……直到根结点【必为x的祖先】

​ 递归条件,先找到值为x的结点,并标记已找到,可以向上返回;左右子树都需要找x,直到找到

​ 终止条件,叶结点返回空;找到x就不用再找,否则继续找

注:一旦找到x后,flag就置为1,每次向上返回都是访问本次p的值,而不再进入递归中

void Search_Ancestor(BiTree T,int x){
    BiTree p=T;
    static int flag=0;
    if(p==NULL)		//叶节点就返回空
        return ;
    if(p->data==x)		
        flag=1;
    if(flag==1)			//找到x,开始向上返回
        visit(p);
    
    if(flag==0)
        Search_Ancestor(p->lchild,x);		//左子树中找完,flag==0  表示还要在右子树中找
    if(flag==0)
        Search_Ancestor(p->rchild,x);
}

8、查找p和q的最近公共祖先r

分析:函数功能,找p和q的公共祖先,也就是找到p的路径与找到q的路径类似,左右向上返回值都不为空

​ 递归条件,没有找到p和q就继续找,返回值只有可能为NULL/p/q的值

​ 终止条件,叶子结点都没有找到就返回空,直到找到p和q所在的结点为止,判断p和q的位置【画图分析】

BiTree Search_Ancestor(BiTree T,int x){
    BiTree root=T,left,right;
    if(root==NULL||root==p||root==q)	//对于叶结点,p结点q结点返回值情况,递归终止条件
        return root;
    
    left=Search_Ancestor(root->lchild,x);	//每个左右结点的返回值情况
    right=Search_Ancestor(root->rchild,x);
    
    //对于递归返回值判断
    if(left==NULL&&right==NULL)		//叶子结点的两侧都为空,不是祖先结点
        return NULL;
    if(left!=NULL&&right!=NULL)		//p和q分别在root的异侧
        return root;
    if(left==NULL)		//左结点为空,返回右边不为空的
        return right;
    if(right==NULL)		//右结点为空,返回左边不为空的
        return left;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值