数据结构杂谈(九)——二叉树的遍历

9 二叉树的遍历

文章目录

  • 9 二叉树的遍历
    • 9.1 递归函数基础
    • 9.2 深度优先遍历的实现
    • 9.3 二叉树层次遍历

9.1 递归函数基础


什么是递归?调用自身就是叫递归,如下所示:

void r(){r();
}

我们习惯借用阶梯图来帮助我们理解这些知识。如果是同一层函数体,那么习惯上在同一层阶梯中,如下所示:

image-20220523100809175

如果是像上述的方式一样来写递归函数,我们不难发现这个循环是无限的。故我们需要加一个终止条件,一般来说,我们习惯将终止条件写于函数体的最开头,当符合终止条件时,函数遍不会继续执行,如下所示:

int i = 0;void r()
{if(i<3){++i;r();}
}

当同一层阶梯中有多个执行语句,我们习惯用一个结点来表示它执行的效果,如下所示:

image-20220523101707334

多个递归

在考研中一般不考查一个递归,而是喜好考查多个递归,在这里给出代码示例和阶梯图,望仔细研究。

int i = 0;
void r()
{if(i<2){++i;r();r();}
}

image-20220523102327880


9.2 深度优先遍历的实现


递归遍历

通过上面递归的学习,我想我们可以看穿下面这个恐怖的遍历代码了。

void r(BTNode  p)
{if(p != nullptr){//(1)r(p->lChild);//(2)r(p->rChild);//(3)}
}

对于一下这颗树来说:

image-20220523103227283

结合上述代码的遍历方式,我们可以画出如下的阶梯图。

image-20220523103306637

如果想要先序遍历,结合上一讲我们讲过的原理,可知遍历方式的不同在于经过第几次时去访问该元素。如果在经过第一次时就拿出结点中的元素则为先序遍历,其他遍历懂得都懂,这里不过多赘述,不清楚的可以看看上一讲。

如果想要在代码中使用先序遍历,只需要在前面的框架中将(1)替换为visit§,其中visit()这个函数可以是自己写的访问结点元素的一个函数。

void r(BTNode  p)
{if(p != NULL){visit(p)r(p->rChild);//(2)r(p->lChild);//(3)}
}

如果是中序则将visit()放在(2),后序则放在(3)。


先序遍历非递归

我们在栈那一讲中知道,栈的作用和递归一样。故如果不想使用递归来解决遍历的话,可以考虑用栈来解决这个问题。

入栈完读取数据后就可以出栈。左右孩子入栈时遵循先右后左,这是因为栈是后入先出,只有先右后左出栈时才能先左后右。如下:

image-20220523105745484

我们定义一个栈。则出入栈顺序应该是:

1入栈,1读取数据发现两个孩子结点,1出栈,先右后左,则3进栈后2进栈,而后读2,发现有孩子,故2出栈,5入栈后4入栈,读取4,发现4没有孩子,4出栈,而后5在栈顶,读取5,5没有孩子,5出栈,而后是3,读取3其有孩子,3出栈,7入栈后6入栈,读取6有孩子,6出栈,8入栈,8读取后没有孩子,8出栈,栈顶为7,读取7其无孩子,7出栈。至此先序遍历结束。

我们来看看代码的实现:

void preorderNonrecursion(BTNode *bt)
{if(bt! = NULL){BTNode * Stack[maxSize];//定义顺序栈int top = -1;//定义栈顶指针BTNode *p = NULL;//定义遍历指针Stack[++top] = bt;//根节点入栈while(top != -1){p = Stack[top--];Visit(p);//取元素if(p->rChild != NULL)Stack[++top] = p->rChild;if(p->lChild != NULL)Stack[++top] = p->lChild;}}
}

后序遍历非递归

先将后序遍历的原因是,后序遍历和先序遍历有一些关系。

我们将后序遍历的倒排叫做逆后序。如下图所示:

image-20220602150557070

仔细观察逆后序我们可以发现一件事。先序遍历是先遍历根,然后遍历左子树,然后遍历右子树。而逆后序是先遍历根,然后右子树,最后左子树。

得到逆序后后,我们可以使用C++容器特有resort方法,也可以将得到的元素全部压入栈中然后取出,即可得到后序遍历。

void postorderNonrecursion(BTNode *bt)
{if(bt! = NULL){BTNode * Stack1[maxSize],*Stack2[maxSize];//定义顺序栈int top1,top2 = -1;//定义栈顶指针BTNode *p = NULL;//定义遍历指针Stack1[++top1] = bt;//根节点入栈while(top1 != -1){p = Stack1[top1--];Stack2[++top2] = p;if(p->lChild != NULL)Stack1[++top] = p->lChild;if(p->rChild != NULL)Stack1[++top] = p->rChild;}while(top != -1){p = Stack2[top2--];Visit(p);}}
}

中序遍历非递归化

image-20220602150628486

中序遍历是这么做的:开始直接一直往左的深处走,途中经过的结点全部压入栈,如上图,压入栈的有1,2,4。

而后,出栈并读取栈顶元素。若栈顶元素子树元素非空则入栈,则4出栈,栈顶元素为2。2读取元素并出栈,2有子树,故5入栈,栈中元素为1,5。

5读取元素并出栈,无子树。

1读取元素并出栈,有子树,3入栈。3有左子树,故一直往左子树深处走。途经6,8,故6,8入栈。此时栈中元素3,6,8,栈顶元素为8。

8读取元素并出栈,无子树。

6读取元素并出栈,无子树。

3读取元素并出栈,有子树,故7入栈。

7读取元素并出栈,无子树。

故全程中序遍历元素为:4,2,5,1,8,6,3,7

void inorderNonrecursion(BTNode *bt)
{if(bt! = NULL){BTNode * Stack[maxSize];//定义顺序栈int top = -1;//定义栈顶指针BTNode *p = NULL;//定义遍历指针p = bt;//将遍历指针指向根节点while(top != -1 || p!= nullptr){while(p != nullptr){Stack[++top] = p;p = p->lChild;}if(top != -1){p = Stack[top--];Visit(p);p = p->rChild;}}}
}

9.3 二叉树层次遍历


想要层次遍历二叉树,我们需要辅助队列。

image-20220602150910378

首先是1入队,然后1出队,访问1的左右子树。存在2和3。

2入队后3入队,然后2出队,访问2的左右子树。存在。故4,5入队。

3出队,访问3的左右子树,存在。故6,7入队。

4出队,访问4的左右子树,不存在。

5出队,访问5的左右子树,不存在。

6出队,访问6的左右子树,存在8,8入队。

7出队,访问7的左右子树,不存在。

8出队,访问8的左右子树,不存在。

至此遍历完成。

void level(BTNode * bt)
{if(bt != NULL){int front,rear;BTNode *que[maxSize];front = rear = 0;BTNode *p;rear = (rear +1)%maxSize;que[rear] = bt;while(front != rear){front = (front + 1) % maxSize;p = que[front];Visit(p);if(p->lChild != NULL){rear = (rear + 1)%maxSize;que[rear] = p->lChild;}if(p->rChild != NULL){rear = (rear + 1)%maxSize;que[rear] = p->rChild;}}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/398493.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

洛谷 P3750 [六省联考2017]分手是祝愿

传送门 题解 //Achen #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<vector> #include<cstdio> #include<queue> #include<cmath> const int N100000,mod100003; #define For(i,a,b)…

解决 error: command 'swig' failed with exit status 1

2019独角兽企业重金招聘Python工程师标准>>> # pip install docker-registry 分析&#xff1a;观察出现的错误&#xff0c;发现最开始报错的地方提示不能找到openssl的.h头文件。一般.h头文件都是放到/usr/inclue目录下的&#xff0c;而且头文件所在的安装包一般叫…

Android安全-SO动态库注入

2019独角兽企业重金招聘Python工程师标准>>> 关于这方面技术&#xff0c;网上已经有大把的实现。在此&#xff0c;我只是记录下自己的学习过程。 0x1 原理 所谓的SO注入就是将代码拷贝到目标进程中&#xff0c;并结合函数重定向等其他技术&#xff0c;最终达到监控或…

BUG日志-2022.7.12——关于VSCode感叹号无法生成HTML骨架问题

解决办法&#xff1a;放弃使用多年的!&#xff0c;而采用html:5的形式。 原因&#xff1a;好像是因为VScode已经更新了 好多扩展也失效了。

hadoop 入门实例【转】

原文链接&#xff1a;http://www.cnblogs.com/xia520pi/archive/2012/06/04/2534533.html 1、数据去重 "数据去重"主要是为了掌握和利用并行化思想来对数据进行有意义的筛选。统计大数据集上的数据种类个数、从网站日志中计算访问地等这些看似庞杂的任务都会涉及数据…

AWS安装CDH5.3-CentOS6.4中关键操作步骤

1、在AWS masternode 上下载cloudera-manager-installer.bin安装包 [rootip-172-21-42-114 ~]# wget http://archive.cloudera.com/cm5/installer/latest/coludera-manager-installer.bin 此时会提示&#xff1a;-bash: wget: command not find 所以要现安装wget命令 [rootip-1…

You have an error in your SQL syntax; check the manual that corresponds to...

问题缘由&#xff1a; 使用datagrip插入数据时发生报错 使用插入语句为&#xff1a; insert into ev_name (‘username’,‘password’) values (‘admin’,‘123456’); 错误提示&#xff1a; You have an error in your SQL syntax; check the manual that corresponds to …

getaddrinfo ENOTFOUND 127.0.0.1:3306

错误缘由&#xff1a; 在写nodejs项目时&#xff0c;连接数据库发现找不到数据库。 解决&#xff1a; 发现数据库连接池没有设置端口号&#xff0c;需设置端口号3306。 解决效果&#xff1a; 成功解决

一幅长文细学node.js——一幅长文系列

文章目录1 Node.js概述1.1 初识Node.js1.2 Node.js简介1.3 Node.js安装1.4 使用Node.js运行JS代码2 fs文件系统模块2.1 读取文件2.2 写入文件2.3 路径问题3 Path路径模块3.1 Path模块概述3.2 路径拼接3.3 获取路径的文件名4 Http模块4.1 Http概述4.2 服务器相关的概念4.3 创建W…

一幅长文细学GaussDB(一)——数据库介绍

文章目录1 数据库介绍1.1 数据库技术1.2 数据库技术发展史数据库技术产生和发展数据库三个阶段比较数据库系统优势层次模型网状模型关系模型关系数据库产品历史结构化查询语言SQL面向对象数据模型&#xff08;OO模型&#xff09;数据管理技术的新挑战NoSQL技术特点和类型主要No…

Unity 通过Unity Admob Plugin插件集成admob教程

原创&#xff1a;officemaster.cn下载Unity Admob Demo&#xff0c;插件里面包含Admob_Unity_Demo.unitypackage 插件文件AdmobPluginRes 是Admob 的ios sdk和插件使用样例代码打开样例代码可以看到代码里面如何使用Unity Admob插件把Admob Unity插件添加进unity工程1. 打开Un…

一幅长文细学华为MRS大数据开发(一)——大数据时代的挑战和机遇

文章目录1 大数据时代的挑战和机遇1.1 大数据基础概念大数据时代的发展大数据定义大数据的4V大数据处理和传统数据处理的差异并行计算相关知识1.2 大数据应用领域大数据金融应用大数据教育应用大数据公共安全应用大数据交通规划应用1.3 大数据计算计算任务的分类大数据应用的主…

一幅长文细学华为MRS大数据开发(二)——HDFS分布式文件系统和ZooKeeper

文章目录2 HDFS分布式文件系统和ZooKeeper2.1 HDFS概述以及应用场景HDFS概述HDFS应用场景HDFS不适合的场景2.2 HDFS相关概念计算机集群结构基本系统架构块NameNode和DataNodes2.3 HDFS体系架构HDFS体系架构概述HDFS命名空间管理通信协议客户端HDFS单名称节点体系结构的局限性心…

BOS12——多对多添加方法,多对多页面需要字段问题(不多的话直接提供get方法),修改Realm中授权方法(查询数据库),缓存Java对象的方法,加载左侧菜单(ztree提供pId)...

1、多对多添加方法 Override public void add(Role model, String functionIds) {// 1.先将角色保存到数据库roleDao.save(model);// 2.为角色添加权限&#xff08;一定要坚持映射文件中是否inverse&#xff09;if (StringUtils.isNotBlank(functionIds)){String[] functionIdL…

一幅长文细学JavaScript(七)——Ajax

文章目录7 Ajax7.1 概述7.1.1 基本概念7.1.2 网络通信开发者工具7.1.3 网页请求数据的方式7.1.4 资源的请求方式7.2 JQuery中的Ajax7.2.1 基本知识7.2.2 了解jQuery的Ajax7.2.3 $.get()7.2.4 $.post()7.2.5 $.ajax()7.3 接口7.3.1 接口概念7.3.2 接口测试工具7.4 form表单7.4.1…

Linux系统检查查看桌面环境

Linux的桌面系统系统多达十几种&#xff0c;像gnome、kde、mate、cinnamon、lxde、xfce、jwm等。比较常用的一般是gnome、kde、xfce等。那么如何判断Linux系统安装了哪种桌面环境组件呢&#xff1f;下面总结了一些检查桌面环境的方法&#xff1a; 方法1&#xff1a;env | grep…

一幅长文细学JavaScript(二)——ECMAScript

2 基本程序设计结构 摘要 ​ 对于学习JS的程序员来说&#xff0c;一定是具备了一定的编程功底的&#xff0c;故在下面的概述中&#xff0c;我们不再提及一些简单的概念。 声明&#xff1a;在使用本文的代码时&#xff0c;为了避免文章冗长&#xff0c;我只附上了script标签内的…

HashMap源码剖析

无论是在平时的练习还是项目当中&#xff0c;HashMap用的是非常的广&#xff0c;真可谓无处不在。平时用的时候只知道HashMap是用来存储键值对的&#xff0c;却不知道它的底层是如何实现的。 一、HashMap概述 HashMap基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作…

Android成长日记-Android监听事件的方法

1. Button鼠标点击的监听事件 --setOnClickListener 2. CheckBox, ToggleButton , RadioGroup的改变事件 --setOnCheckedChangeListener Eg: 3. onPageChangeListener() ----用来监控ViewPager滑到第几页转载于:https://www.cnblogs.com/boy1025/p/4301956.html