详解二叉树的遍历问题(前序、后序、中序、层序遍历的递归算法及非递归算法及其详细图示)


二叉树的遍历

 所谓二叉树的遍历,是指按照某条搜索路径访问树中的每个结点,使得每个结点均被访问一次,且仅被访问一次。
 由二叉树的递归定义可知,遍历一棵二叉树便要决定对根结点N、左子树L和右子树R的访问顺序。按照先遍历左子树再遍历右子树的原则,常见的遍历次序有先序(NLR)中序(LNR)后序(LRN)三种遍历算法,其中“序”指的是根结点在何时被访问

 二叉树的结点类型描述如下:

typedef struct BNode{
    char data;
    struct BNode *lchild;
    struct BNode *rchild;
}BNode;

 在给出具体算法之前,先给出算法中的实例,以供参考。
 需要遍历的二叉树:
在这里插入图片描述

 前序遍历:ABDECFG
 中序遍历:DBEAFCG
 后序遍历:DEBFGCA
 层序遍历:ABCDEFG



先序遍历(前序遍历)

先序遍历(PreOrder)的操作过程如下:
若二叉树为空,则什么也不做,否则:
1)访问根结点;
2)先序遍历左子树;
3)先序遍历右子树;


先序遍历的递归算法
void PreOrder(BNode *root){
    if(root != NULL){
        printf("%c ",root->data); //访问根结点
        PreOrder(root->lchild); //递归遍历左子树
        PreOrder(root->rchild); //递归遍历右子树
    }
}

先序遍历的非递归算法

 将二叉树的先序遍历的递归算法转换成非递归算法时,需要借助
声明一个遍历指针p,初始指向根结点。
如果p指向的结点不为空,则访问该结点,输出结点值,并将该根结点的地址入栈,然后将p指向根结点的左子树。
如果p指向的结点为空,那么用top记录栈顶元素,将栈顶结点出栈,然后再往栈顶结点的右子树走。

 在先序遍历中,栈中的元素都是自己本身和自己的左孩子都访问过了,而右孩子还没有访问到的结点。

 实现代码:

void PreOrder2(BNode *root){
    stack<BNode *> treenode; //申请一个栈,栈中保存的是结点地址
    BNode *p = root; //p是遍历指针
    BNode *top; //指向当前的栈顶元素
    while(p != NULL || !treenode.empty()){//p不空或者栈不空时循环
        if(p != NULL){
            printf("%c ",p->data); //访问当前根结点
            treenode.push(p); //结点进栈
            p = p->lchild; //指向当前结点的左子树
        }else{
            top = treenode.top(); //栈顶结点
            treenode.pop(); //出栈
            p = top->rchild; //再往右子树走
        }
    }
}

先序遍历的非递归算法图示

 首先给出需要遍历的二叉树:
在这里插入图片描述

1)创建一个空栈,初始化遍历指针p,其初始值指向根结点*root,并声明一个指针top,用于以后存储栈顶元素。

  注意:为了便于理解,在以下的图中栈中的内容为该结点的结点值,而在具体实现代码中,栈中存放的则是该结点地址。
在这里插入图片描述

2)p不空或栈不空时循环。

2.1)若p不为空,则访问当前根结点A,然后将该结点进栈,最后将p指向该根结点的左子树B。
在这里插入图片描述

2.2)若p不为空,则访问当前根结点B,然后将该结点进栈,最后将p指向该根结点的左子树D。
在这里插入图片描述

2.3)若p不为空,则访问当前根结点D,然后将该结点进栈,最后将p指向该根结点的左子树。
在这里插入图片描述

2.4)若p为空,则将top指向栈顶的结点D,然后将结点D出栈,最后将遍历指针p指向该栈顶结点D的右子树。
在这里插入图片描述

2.5)若p为空,则将top指向栈顶结点B,然后将栈顶结点B出栈,最后将遍历指针p指向栈顶结点B的右子树E。
在这里插入图片描述

2.6)若p不为空,则访问当前根结点E,然后将该结点进栈,最后将p指向该结点的左子树。
在这里插入图片描述

2.7)若p为空,则将top指向栈顶结点E,然后将栈顶结点E出栈,最后将遍历指针p指向该栈顶结点E的右子树。
在这里插入图片描述

2.8)若p为空,则将top指向当前栈顶结点A,然后将栈顶结点A出栈,最后将遍历指针p指向该栈顶结点A的右子树C。
在这里插入图片描述

2.9)若p不为空,则访问当前根结点C,并将该结点入栈,最后将p指向该根结点C的左子树F。
在这里插入图片描述

2.10)若p不为空,则访问当前根结点F,并将该结点入栈,最后将p指向该根结点F的左子树。
在这里插入图片描述

2.11)若p为空,则将top指向栈顶结点F,然后将栈顶结点F出栈,最后将遍历指针p指向该栈顶结点F的右子树。
在这里插入图片描述

2.12)若p为空,则将top指向栈顶结点C,然后将该栈顶结点C出栈,最后将遍历指针p指向该栈顶结点C的右子树G。
在这里插入图片描述

2.13)若p不为空,则访问当前根结点G,并将该结点入栈,最后将p指向该根结点G的左子树。
在这里插入图片描述

2.14)若p为空,则将top指向栈顶结点G,然后将栈顶结点G出栈,最后将遍历指针p指向该栈顶结点G的右子树。
在这里插入图片描述

3)当p为空且栈为空时,结束循环,遍历完成。
在这里插入图片描述

 所以当前树的前序遍历为ABDECFG。

 显然非递归算法的执行效率要高于递归算法。



中序遍历

中序遍历(InOrder)的操作过程如下:
若二叉树为空,则什么也不做,否则:
1)中序遍历左子树;
2)访问根结点;
3)中序遍历右子树。


中序遍历的递归算法
void InOrder(BNode *root){
    if(root != NULL){
        InOrder(root->lchild); //递归遍历左子树
        printf("%c ",root->data); //访问根结点
        InOrder(root->rchild); //递归遍历右子树
    }
}

中序遍历的非递归算法

 对于中序遍历的递归算法,将其转换为非递归算法,需要借助

先扫描(并不是访问)根结点的所有左子树并将它们一一进栈,然后出栈一个结点*p(显然结点*p没有左孩子结点或左孩子结点均已访问过),访问结点*p。
然后扫描该结点的右孩子结点,将其进栈,再扫描该右孩子结点的所有左结点并一一进栈,如此继续,直到栈空为止。

 中序遍历与前序遍历不同的是,前序遍历时,栈中保存的元素是右子树还没有被访问到的结点地址,而中序遍历时,栈中保存的元素是结点自身和它的右子树中没有被访问到的结点地址。

 实现代码:

void InOrder2(BNode *root){
    stack<BNode *> treenode; //申请一个栈
    BNode *p = root; //p是遍历指针
    while(p != NULL || !treenode.empty()){ //栈不空或p不空时循环
        if(p != NULL){ //根指针进栈,遍历左子树
            treenode.push(p); //每次遇到非空二叉树先向左走
            p = p->lchild;
        }else{ //根指针退栈,访问根结点,遍历右子树
            p = treenode.top(); //p指向栈顶元素
            treenode.pop(); //退栈
            printf("%c ",p->data); //访问当前根结点
            p = p->rchild; //再向右子树走
        }
    }
}

细心的读者可能注意到,中序遍历时没有重新声明一个指针top记录栈顶元素,而是直接用p来记录,这两种方法都是可以的(最终p都会指向栈顶结点的右孩子)。


中序遍历非递归算法的图示

 首先给出需要遍历的二叉树:
在这里插入图片描述

1)创建一个空栈,并初始化遍历指针p,初始值指向根结点*root。

  注意:为了便于理解,在以下的图中栈中的内容为该结点的结点值,而在具体实现代码中,栈中存放的则是该结点地址。
在这里插入图片描述

2)p不空或栈不空时循环。

2.1)如果p不空,则根指针进栈,p指向该根结点A的左子树B。
在这里插入图片描述

2.2)如果p不空,则当前根指针进栈,p指向该根结点B的左子树D。
在这里插入图片描述

2.3)如果p不空,则当前根结点D进栈,p指向该根结点D的左子树。
在这里插入图片描述

2.4)如果p为空,则当前栈顶根结点D退栈,访问当前根结点D,再向右子树走。
在这里插入图片描述

2.5)如果p为空,则栈顶根结点B退栈,访问该根结点B,然后再向右子树E走。
在这里插入图片描述

2.6)如果p不空,则将当前根结点E入栈,p指向该根结点E的左子树。
在这里插入图片描述

2.7)如果p为空,则当前栈顶的根结点E退栈,访问该根结点E,然后再往右子树走。
在这里插入图片描述

2.8)如果p为空,则当前栈顶的根结点A退栈,访问该根结点A,然后再往右子树C走。
在这里插入图片描述

2.9)如果p不空,则将根结点F入栈,然后将p指向该根结点C的左子树F。
在这里插入图片描述

2.10)如果p不空,则将该根结点F入栈,将p指向该根结点F的左子树。
在这里插入图片描述

2.11)如果p为空,则将当前栈顶的根结点F退栈,访问该根结点F,然后再往右子树走。
在这里插入图片描述

2.12)如果p为空,则当前栈顶的根结点C退栈,访问该根结点C,然后再往右子树G走。
在这里插入图片描述

2.13)如果p不空,则将该根结点G入栈,然后将p指向该结点G的左子树。
在这里插入图片描述

2.14)如果p为空,则将当前栈顶的根结点G出栈,访问该根结点G,然后再往右子树走。
在这里插入图片描述

3)如果p为空且栈也为空,则退出循环,遍历完成。
在这里插入图片描述

 所以当前树的中序遍历为DBEAFCG。

 显然非递归算法的执行效率要高于递归算法。



后序遍历

后序遍历(PostOrder)的操作过程如下:
若二叉树为空,则什么也不做,否则:
1)后序遍历左子树;
2)后序遍历右子树;
3)访问根结点。


后序遍历的递归算法
void PostOrder(BNode *root){
    if(root != NULL){
        PostOrder(root->lchild); //递归遍历左子树
        PostOrder(root->rchild); //递归遍历右子树
        printf("%c ",root->data); //访问根结点
    }
}

后序遍历的非递归算法

 后序遍历的递归算法转换为非递归算法时,也是需要借助来保存元素。
从根结点开始,沿着左子树一直往下走,直到左子树为空。
然后再用top指针记录栈顶元素:
如果top指针指向的结点右子树为空或者last指向的结点,则说明top的右子树不存在或者已经遍历过了,那么弹出栈顶元素,访问top指向的结点,输出该结点值。
否则,说明是从左子树返回当前根结点的,那么就将遍历指针p指向top的右子树,去遍历右子树。

 栈中保存的是它的右子树和自身都没有被遍历到的结点,与中序遍历不同地方的是,先访问右子树,在访问完右子树后再输出根结点的值。这就需要多一个last指针来指向上一次访问到的结点,用来确认是从根结点的左子树还是右子树返回的

【tips】访问一个结点*p时,栈中结点恰好是*p结点的所有祖先。从栈底到栈顶结点再加上*p结点,刚好构成从根结点到*p结点的一条路径。在很多题目中都运用了这一特性求解,如根结点到某结点的路径、求两个结点的最近公共祖先等,都可以利用这个思路求解。

 实现代码:

void PostOrder2(BNode *root){
    stack<BNode *> treenode;
    BNode *p = root; //p是遍历指针
    BNode *top,*last = NULL; 
    while(p != NULL || !treenode.empty()){ //p不空或者栈不空时循环
        if(p != NULL){ //走到最左边的结点
            treenode.push(p); //当前根指针入栈
            p = p->lchild; //向左子树走
        }else{ //向右
            top = treenode.top(); //top栈顶指针
            if(top->rchild == NULL || top->rchild == last){ //右子树为空或者已经被遍历过
                treenode.pop(); //栈顶指针出栈
                printf("%c ",top->data); //输出该结点值
                last = top; //last指针指向当前top指针指向的结点
                p = NULL; //重置p指针
            }else{ //右子树存在且未被访问过
                p = top->rchild; //p指向当前top指针指向的结点的右子树
                //下面这两句可以不用写,与第一个if是对应的,主要就是找到右子树的最左结点;但加上可以减少一次循环
                //treenode.push(p); //压入栈
                //p = p->lchild; //再走向最左
            }
        }
    }
}

后序遍历非递归算法的图示

 首先给出需要遍历的二叉树:
在这里插入图片描述

1)创建一个空栈,初始化遍历指针p,其初始值指向根结点*root,并声明一个指针top,用于以后存储栈顶元素。声明一个last指针,初始值为空。

  注意:为了便于理解,在以下的图中栈中的内容为该结点的结点值,而在具体实现代码中,栈中存放的则是该结点地址。

在这里插入图片描述

2)当p不空或栈不空时循环。

2.1)若p不为空,则当前根结点A进栈,p指向当前根结点A的左子树B。
在这里插入图片描述

2.2)若p不为空,则将当前根结点B入栈,并将p指向当前根结点B的左子树D。
在这里插入图片描述

2.3)若p不为空,则将当前根结点D入栈,并将p指向当前根结点D的左子树。
在这里插入图片描述

2.4)若p为空,则top等于当前栈顶元素,指向结点D(注意此时结点D还未出栈)。
 如果top指向的结点D的右子树为空或者为last指针指向的结点,说明top的右子树不存在或者是已经遍历过,则栈顶结点D出栈,访问该结点last指针指向当前top指向的结点
在这里插入图片描述

2.5)若p为空,则top等于当前栈顶元素,指向结点B
 如果top指向的结点B的右子树既不空也不是last指针指向的结点,那么p就指向当前top指向的结点B的右子树E
在这里插入图片描述

2.6)若p不为空,则将当前根结点E入栈,p指向当前根结点E的左子树。
在这里插入图片描述

2.7)若p为空,则top等于当前栈顶元素,指向结点E
 如果top指向结点的的右子树为空或者为last指针指向的结点,说明top的右子树不存在或者是已经遍历过,则栈顶结点E出栈,访问该结点last指针指向当前top指向的结点
在这里插入图片描述

2.8)如果p为空,则top等于当前栈顶元素,指向结点B。
 如果top指向的结点的右孩子为空或者为last指向的结点,说明top的右子树不存在或者已经遍历过,则栈顶结点B出栈,访问该结点Blast指针指向当前top指向的结点
在这里插入图片描述

2.9)如果p为空,则top等于栈顶元素,指向结点A。
 如果top指向的结点A的右子树不为空且不是last指针指向的结点,则p指向当前top指向的结点的右子树C
在这里插入图片描述

2.10)若p不为空,则将当前根结点C入栈,将p指向当前根结点C的左子树F。
在这里插入图片描述

2.11)若p不为空,则将当前根结点F入栈,将p指向当前根结点F的左子树。
在这里插入图片描述

2.12)若p为空,则top等于当前栈顶元素,指向结点F。
 如果top指向的结点F的右子树为空或者为last指向的结点,则栈顶结点F出栈,访问该结点Flast指针指向当前top指向的结点
在这里插入图片描述

2.13)若p为空,则top等于当前栈顶元素,指向结点C。
 如果top指向的结点C的右子树不为空且不为last指向的结点,则p指向当前top指向的结点的右子树G。
在这里插入图片描述

2.14)若p不为空,则将当前根结点G入栈,将p指向当前根结点G的左子树。
在这里插入图片描述

2.15)若p为空,则top等于当前栈顶元素,指向结点G。
 如果top指向的结点的右子树为空或者为last指向的结点,则说明top指向的结点的右子树不存在或者已经遍历过,则栈顶结点G出栈,访问该结点G,将last指针指向当前top指向的结点
在这里插入图片描述

2.16)若p为空,则top等于当前栈顶元素,指向结点C。
 如果top指向的结点C的右子树为空或者等于last指向的结点,则栈顶结点C出栈,访问该结点C,将last指针指向top指向的结点
在这里插入图片描述

2.17)若p为空,则top等于当前栈顶元素,指向结点A。
 如果top指向的结点A的右子树为空或者为last指针指向的结点,则将栈顶结点A出栈,访问该结点A,最后将last指针指向top指向的结点A
在这里插入图片描述

3)当p为空且栈为空时,结束循环,遍历完成。
在这里插入图片描述

 所以当前树的后序遍历为DEBFGCA。

 显然非递归算法的执行效率要高于递归算法。


层序遍历

 如下图所示即为二叉树的层序遍历,即按照从上到下、从左到右的顺序依次访问每一层,直至每个结点被访问且仅被访问一次。

在这里插入图片描述

 要进行层序遍历,需要借助一个队列
 先将二叉树的根结点入队,然后出队,访问该结点;若它有左子树,则将左子树根结点入队;若它有右子树,则将右子树根结点入队。然后出队,对出队结点进行访问,如此反复,直到队列为空。

void LevelOrder(BNode *root){
    queue<BNode *> treenode; //队列存储结点
    if(root != NULL)
        treenode.push(root); //根结点入队
    while(!treenode.empty()){
        BNode *p = treenode.front(); 
        treenode.pop(); //根结点出队
        printf("%c ",p->data); //输出队首元素,即当前访问的结点值
        if(p->lchild != NULL){
            treenode.push(p->lchild);//如果有左子树,则将左子树的根结点入队
        }
        if(p->rchild != NULL){
            treenode.push(p->rchild);//如果有右子树,则将右子树的根结点入队
        }
    }
}

层序遍历的算法图示:

 首先给出需要遍历的二叉树:
在这里插入图片描述

1)声明一个空队列用来存储结点指针。当根结点不为空时,根结点入队。
 为了便于理解,以下队列中的元素均为该结点值.
在这里插入图片描述

2)当队列不空时循环。
2.1)声明一个指针p,p指向当前队首元素所指向的结点。将当前根结点出队,并访问该根结点A。
 如果该根结点有左子树,将左子树的根结点B入队。
 如果该根结点有右子树,将右子树的根结点C入队。
在这里插入图片描述

2.2)p指向队首元素所指向的结点B。将队首根结点出队,并访问该结点B。
 如果该根结点有左子树,将左子树的根结点D入队。
 如果该根结点有右子树,将右子树的根结点E入队。
在这里插入图片描述

2.3)p指向队首元素所指向的结点C。将队首根结点出队,并访问该结点C。
 如果该根结点有左子树,将左子树的根结点F入队。
 如果该根结点有右子树,将右子树的根结点G入队。
在这里插入图片描述

2.4)p指向队首元素所指向的结点D。将队首根结点出队,并访问该结点D。
 由于该根结点没有左子树,也没有右子树,故if中的语句不执行。
在这里插入图片描述

2.5)p指向队首元素所指向的结点E。将队首根结点出队,并访问该结点E。
 由于该根结点没有左子树,也没有右子树,故if中的语句不执行。
在这里插入图片描述

2.6)p指向队首元素所指向的结点F。将队首根结点出队,并访问该结点F。
 由于该根结点没有左子树,也没有右子树,故if中的语句不执行。
在这里插入图片描述

2.7)p指向队首元素所指向的结点G。将队首根结点出队,并访问该结点G。
 由于该根结点没有左子树,也没有右子树,故if中的语句不执行。
在这里插入图片描述

3)队列为空时,结束循环,遍历完成。
在这里插入图片描述

 故该二叉树的层序遍历为ABCDEFG。


二叉树的建立

二叉树的前序遍历存储在字符数组str中,根据这个字符序列建立一个二叉树,其中’#'表示空结点。

//建立二叉树
int i; //标记当前处理的字符位置
char str[100]; //读取的字符串
BNode *BuildTree(){
    if(str[i] == '#'){ //空结点
        i++; //准备处理下一个字符
        return NULL;
    }else{
        //新建一个结点
        BNode *p = (BNode *)malloc(sizeof(BNode));
        p->lchild = NULL;
        p->rchild = NULL;
        p->data = str[i];
        i++;
        p->lchild = BuildTree(); //创建左子树
        p->rchild = BuildTree(); //创建右子树
        return p; //最后返回根结点指针
    }
    return NULL;
}

 如果对建树的过程有所疑问,可以参考文章二叉树遍历应用之根据前序遍历建树

完整代码及实例

完整代码:

#include<bits/stdc++.h>
using namespace std;

typedef struct BNode{
    int data;
    struct BNode *lchild;
    struct BNode *rchild;
}BNode;

//建立二叉树
const int N = 100;
char str[N]; //读取的字符串
int i; //标记当前处理的字符位置
BNode *BuildTree(){
    if(str[i] == '#'){ //空结点
        i++; //准备处理下一个字符
        return NULL;
    }else{
        //新建一个结点
        BNode *p = (BNode *)malloc(sizeof(BNode));
        p->lchild = NULL;
        p->rchild = NULL;
        p->data = str[i];
        i++;
        p->lchild = BuildTree(); //创建左子树
        p->rchild = BuildTree(); //创建右子树
        return p; //最后返回根结点指针
    }
    return NULL;
}


//前序遍历
void PreOrder(BNode *root){
    if(root != NULL){
        printf("%c ",root->data);
        PreOrder(root->lchild);
        PreOrder(root->rchild);
    }
}

//前序遍历的非递归算法
void PreOrder2(BNode *root){
    stack<BNode *> treenode; //申请一个栈
    BNode *p = root; //p是遍历指针
    BNode *top; //指向当前的栈顶结点
    while(p != NULL || !treenode.empty()){//p不空或者栈不空时循环
        if(p != NULL){
            printf("%c ",p->data); //访问当前根结点
            treenode.push(p); //根结点进栈
            p = p->lchild; //指向当前结点的左子树
        }else{
            top = treenode.top(); //栈顶结点
            treenode.pop(); //出栈
            p = top->rchild; //再往右子树走
        }
    }
}

//中序遍历
void InOrder(BNode *root){
    if(root != NULL){
        InOrder(root->lchild);
        printf("%c ",root->data);
        InOrder(root->rchild);
    }
}
//中序遍历非递归
void InOrder2(BNode *root){
    stack<BNode *> treenode; //申请一个栈
    BNode *p = root; //p是遍历指针
    while(p || !treenode.empty()){ //栈不空或p不空时循环
        if(p != NULL){ //根指针进栈,遍历左子树
            treenode.push(p); //每次遇到非空二叉树先向左走
            p = p->lchild;
        }else{ //根指针退栈,访问(当前)根结点,遍历右子树
            p = treenode.top(); //p指向栈顶元素
            treenode.pop(); //退栈
            printf("%c ",p->data); //访问当前根结点
            p = p->rchild; //再向右子树走
        }
    }
}

//后序遍历
void PostOrder(BNode *root){
    if(root != NULL){
        PostOrder(root->lchild);
        PostOrder(root->rchild);
        printf("%c ",root->data);
    }
}
//后序遍历的非递归算法
void PostOrder2(BNode *root){
    stack<BNode *> treenode;
    BNode *p = root; //p是遍历指针
    BNode *top,*last = NULL; 
    while(p != NULL || !treenode.empty()){ //p不空或者栈不空时循环
        if(p != NULL){
            treenode.push(p); //当前根指针入栈
            p = p->lchild; //向左子树走
        }else{
            top = treenode.top(); //top栈顶指针
            if(top->rchild == NULL || top->rchild == last){
                treenode.pop(); //栈顶指针出栈
                printf("%c ",top->data); //输出该结点值
                last = top; //last指针指向当前top指针指向的结点
                p = NULL;
            }else{ //右子树存在且未被访问过
                p = top->rchild; //p指向当前top指针指向的结点的右子树
            }
        }
    }
}

//层序遍历
void LevelOrder(BNode *root){
    queue<BNode *> treenode; //队列存储结点
    if(root != NULL)
        treenode.push(root); //根结点入队
    while(!treenode.empty()){
        BNode *p = treenode.front(); 
        treenode.pop(); //根结点出队
        printf("%c ",p->data); //输出队首元素,即当前访问的结点值
        if(p->lchild != NULL){
            treenode.push(p->lchild);//如果有左子树,则将左子树的根结点入队
        }
        if(p->rchild != NULL){
            treenode.push(p->rchild);//如果有右子树,则将右子树的根结点入队
        }
    }
}

int main(){
    while(scanf("%s",str) != EOF){
        i = 0;
        BNode *root = BuildTree();
        printf("递归前序遍历结果: ");
        PreOrder(root);
        printf("\n");
        printf("非递归前序遍历结果:");
        PreOrder2(root);
        printf("\n");
        printf("递归中序遍历结果: ");
        InOrder(root);
        printf("\n");
        printf("非递归中序遍历结果:");
        InOrder2(root);
        printf("\n");
        printf("递归后序遍历结果: ");
        PostOrder(root);
        printf("\n");
        printf("非递归后序遍历结果:");
        PostOrder2(root);
        printf("\n");
        printf("层序遍历结果: ");
        LevelOrder(root);
    }
    return 0;
}

运行结果:
在这里插入图片描述

说明

 由于文章篇幅较大,所以只给出了非递归遍历的完整过程图示,如果想要了解递归遍历算法的完整过程,请在评论区留言,届时再更新递归算法的完整图示。

  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
好的,您的问题是如何使用 C 语言建立一棵二叉树遍历输出前序后序中序,并交换左右子树以及统计叶子节点个数。 以下是实现该功能的示例代码: ```c #include <stdio.h> #include <stdlib.h> // 定义二叉树节点结构体 struct Node { int value; struct Node* left; struct Node* right; }; // 创建一个新节点 struct Node* createNode(int value) { struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); newNode->value = value; newNode->left = NULL; newNode->right = NULL; return newNode; } // 插入一个新节点 struct Node* insertNode(struct Node* root, int value) { if (root == NULL) { return createNode(value); } if (value < root->value) { root->left = insertNode(root->left, value); } else { root->right = insertNode(root->right, value); } return root; } // 前序遍历 void preOrderTraversal(struct Node* root) { if (root != NULL) { printf("%d ", root->value); preOrderTraversal(root->left); preOrderTraversal(root->right); } } // 中序遍历 void inOrderTraversal(struct Node* root) { if (root != NULL) { inOrderTraversal(root->left); printf("%d ", root->value); inOrderTraversal(root->right); } } // 后序遍历 void postOrderTraversal(struct Node* root) { if (root != NULL) { postOrderTraversal(root->left); postOrderTraversal(root->right); printf("%d ", root->value); } } // 交换左右子树 void swapLeftAndRight(struct Node* root) { if (root != NULL) { struct Node* tempNode = root->left; root->left = root->right; root->right = tempNode; swapLeftAndRight(root->left); swapLeftAndRight(root->right); } } // 统计叶子节点个数 int countLeaves(struct Node* root) { if (root == NULL) { return 0; } if (root->left == NULL && root->right == NULL) { return 1; } else { return countLeaves(root->left) + countLeaves(root->right); } } int main() { // 创建二叉树 struct Node* root = NULL; root = insertNode(root, 4); insertNode(root, 2); insertNode(root, 6); insertNode(root, 1); insertNode(root, 3); insertNode(root, 5); insertNode(root, 7); // 输出遍历结果 printf("前序遍历:"); preOrderTraversal(root); printf("\n"); printf("中序遍历:"); inOrderTraversal(root); printf("\n"); printf("后序遍历:"); postOrderTraversal(root); printf("\n"); // 交换左右子树 swapLeftAndRight(root); // 输出交换后的遍历结果 printf("交换左右子树后的前序遍历:"); preOrderTraversal(root); printf("\n"); // 统计叶子节点个数 int leaves = countLeaves(root); printf("叶子节点个数:%d\n", leaves); return 0; } ``` 这里我们创建了一个二叉树,并实现了前序中序后序遍历以及交换左右子树和统计叶子节点个数的功能。您可以根据您的需要修改该示例代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值