二叉树的遍历(七种方法)

简介: 本章主要通过运用递归与非递归方法分别对二叉树进行遍历,主要分先序遍历、中序遍历、后序遍历以及层次遍历四种情况进行讨论

一. 先序遍历

1.1 递归法

根据二叉树的递归特性,先序遍历二叉树的递归过程如下:
(1)访问根结点
(2)先序遍历左子树
(3)先序遍历右子树

void preorder(BiTree t){
    if(t!=NULL){
        printf("%c",t->data);
        preorder(t->lchild);
        preorder(t->rchild);
    }
} 

1.2 非递归法

从上面的算法中可以看出,先序遍历的递归过程简短而易于理解。依照递归算法执行过程中递归工作栈的状态变化状况可以直接写出相应的非递归算法。非递归算法中借助栈来完成整个遍历过程。算法的基本步骤如下:
(1)当前指针指向根结点。
(2)若结点不为空,访问该结点。
(3)若结点右孩子不为空,则右孩子进栈。
(4)当前指针指向结点左孩子重复步骤(2)~(3),直至左孩子为空。
(5)依次退栈,当前指针指向出栈结点。
(6)若栈非空或当前指针非空,继续步骤(2),直至结束。
由此可以写出先序遍历的非递归算法:

void PreOrder(BiTree t){
    BiTree stack[MAX],q;
    int top=0,i;
    for(i=0;i<MAX;i++){
        stack[i]=NULL;
    }
    q=t;
    while(q!=NULL){
        printf("%c",q->data);
        if(q->rchild!=NULL){
            stack[top++]=q->rchild;
        }
        if(q->lchild!=NULL){
            q=q->lchild;
        }else if(top>0){
            q=stack[--top];
        }else{
            q=NULL;
        }
    }
}

二. 中序遍历

2.1 递归法

中序遍历二叉树的递归过程如下:
(1)中序遍历左子树
(2)访问根结点
(3)中序遍历右子树

void inorder(BiTree t){
    if(t!=NULL){
        inorder(t->lchild);
        printf("%c",t->data);
        inorder(t->rchild);
    }
}

2.2 非递归法

同样也可以利用栈来实现中序遍历的非递归算法,算法的基本步骤如下:
(1)当前指针指向根结点。
(2)当前指针进栈,当前指针指向其左孩子。
(3)重复步骤(2),直至左孩子为空。
(4)依次退栈,输出当前指针所指结点。
(5)将当前指针指向右孩子。
(6)若栈非空或当前指针非空,执行步骤(2),直至结束。

void InOrder(BiTree t){
    BiTree stack[MAX],p;
    int top=0,i;
    for(i=0;i<MAX;i++){
        stack[i]=NULL;
    }
    p=t;
    while(p!=NULL||top>0){
        while(p){
            stack[top++]=p;
            p=p->lchild;
        }
        p=stack[--top];
        printf("%c",p->data);
        p=p->rchild;
    }
}

三. 后序遍历

3.1 递归法

后序遍历二叉树的递归过程如下:
(1)后序遍历左子树
(2)后序遍历右子树
(3)访问根结点

void postorder(BiTree t){
    if(t!=NULL){
        postorder(t->lchild);
        postorder(t->rchild);
        printf("%c",t->data);
    }
}

3.2 非递归法

后序遍历的非递归算法比先序和中序的非递归算法复杂一些。按照后序遍历的过程,在遍历左子树之前,就要将根结点的地址保存在栈中,以便能在左子树遍历完成之后,从栈中弹出根结点地址,通过根结点走到右子树;但因为还没有遍历右子树,所以类似的,在遍历右子树之前,必须再次将根结点压入栈中,在遍历完右子树后,才能访问它。由于一个结点要进栈、出栈两次,就要对它是第一次进栈还是第二次进栈的情况加以区分。可以通过为结点增加标志位或与刚刚访问的结点相比较的方法,达到判断该结点是否应该访问的目的。
因此要引入一个标志栈flag[max],对应结点的标志为0,表明左子树遍历,标志为1,表明右子树遍历。后序遍历二叉树的非递归算法步骤如下:
(1)当前指针指向根结点。
(2)当前指针不为空,进栈,所对应标志为0,当前指针指向其左孩子,重复直至左孩子为空。
(3)判断栈顶元素的标志,若为1,则依次退栈,输出结点,直至标志为0。
(4)若栈顶元素的标志为0,将当前指针指向栈顶元素的右孩子,并置标志为1,进栈;若当前指针为空,则退栈,输出结点,直至标志为0。
(5)若栈非空或当前指针非空,执行步骤(2),直至结束。

void PostOrder(BiTree t){
    BiTree stack[MAX],q;
    int i,top=0,flag[MAX];
    for(i=0;i<MAX;i++){
        stack[i]=NULL;
        flag[i]=0;
    }
    q=t;
    while(q!=NULL||top!=0){
        if(q!=NULL){
            stack[top]=q;
            flag[top]=0;
            top++;
            q=q->lchild;
        }else{
            while(top){
                if(flag[top-1]==0){
                    q=stack[top-1];
                    q=q->rchild;
                    flag[top-1]=1;
                    break;
                }else{
                    q=stack[--top];
                    printf("%c",q->data);
                }
            }
        }
        if(top==0) break;
    }    
}

四. 层次遍历

二叉树的层次遍历是指从二叉树的根结点开始,从上往下逐层遍历,同一层中的结点按从左往右的顺序访问。层次遍历需要借助一个辅助队列,利用队列先进先出的特性,存放访问过的结点,以便下一层继续按照结点的左右次序访问它们的孩子。
层次遍历的基本步骤如下:
(1)从根结点开始,访问根结点,并进行入队操作。
(2)若队不为空,就进行出队操作,访问出队结点的左右孩子,并入队。继续循环,直至队列为空。
层次遍历二叉树的算法如下:、

void CcOrder(BiTree t){
    BiTree p;
    SqQueue qlist,*q;
    q=&qlist;
    q->front=0;
    q->rear=0;
    p=t;
    if(p!=NULL){
        printf("%c",p->data);
        q->data[q->rear]=p;
        q->rear=(q->rear+1)%MAX;
        while(q->front!=q->rear){
            p=q->data[q->front];
            q->front=(q->front+1)%MAX;
            if(p->lchild!=NULL){
                printf("%c",p->lchild->data);
                q->data[q->rear]=p->lchild;
                q->rear=(q->rear+1)%MAX;
            }
            if(p->rchild!=NULL){
                printf("%c",p->rchild->data);
                q->data[q->rear]=p->rchild;
                q->rear=(q->rear+1)%MAX;
            }
        }
    }
}
目录
相关文章
|
消息中间件 Java 大数据
RocketMQ
【8月更文挑战第29天】RocketMQ
318 15
|
搜索推荐 数据挖掘 API
探讨拼多多商品 API 接口:运用及收益
拼多多以其独特的商业模式迅速崛起,成为电商领域的重要力量。拼多多商品API接口为开发者和企业提供了一套强大的工具,能够获取丰富的商品信息,包括基本资料、价格详情、库存数据、商品图片、销售属性、销量数据及用户评价等。该接口在电商平台拓展、数据分析、移动应用开发和营销推广等多个领域展现出卓越的应用价值,不仅促进了销售额和利润的增长,还优化了用户体验,积累了宝贵的数据资产,为企业战略决策提供了重要依据。
1124 5
|
11月前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
230 5
|
程序员 Python 容器
python 中的 collections 模块:常用数据结构和工具详解
python 中的 collections 模块:常用数据结构和工具详解
119 2
|
SQL 数据管理 关系型数据库
数据管理DMS操作报错合集之执行列表模糊搜索,无法搜到对应表的记录,是什么原因
数据管理DMS(Data Management Service)是阿里云提供的数据库管理和运维服务,它支持多种数据库类型,包括RDS、PolarDB、MongoDB等。在使用DMS进行数据库操作时,可能会遇到各种报错情况。以下是一些常见的DMS操作报错及其可能的原因与解决措施的合集。
177 1
|
机器学习/深度学习 人工智能 Linux
探索操作系统的多任务处理能力
本文将深入探讨操作系统如何处理多任务,包括其背后的原理、实现方式以及优化策略。我们将通过分析不同的操作系统,如Windows、Linux和Mac OS,来比较它们在处理多任务时的性能和效率。此外,我们还将讨论一些可能的未来发展趋势,如人工智能在操作系统中的应用。
|
存储 安全 Devops
爆测一周!22年必看最细致代码托管工具测评
网上代码托管选型的文章不少,不过大多内容有点久远,很多最新的平台没有包括进来,个人花了大概一个星期的时间,把目前市面上比较火的代码托管平台(开源托管平台:Github、Gitee;企业级托管平台:Gitlab、阿里云效Codeup、 腾讯Coding)做了一些比较,比较的维度包括速度、成本、产研工具链完整性、安全、统计报表等,希望可以帮助正在进行代码托管选型的技术同行做决策选型。
1985 0
爆测一周!22年必看最细致代码托管工具测评
|
存储 数据库 Nacos
微服务限流Sentinel讲解(五)
微服务限流Sentinel讲解(五)
251 0
详解CAN总线:CAN总线报文格式—遥控帧
CAN总线上传输的信息称为报文,当总线空闲时任何连接的单元都可以开始发送新的报文。
|
Windows
磨皮软件portraiture最新版下载及使用教程
Portraiture操作界面友好,安装过程简单,是一款相当实用的磨皮滤镜。接下来,我们以Photoshop软件为例,演示一下插件的安装与激活过程吧。
1289 0