二叉树前序中序后序javascript_详解二叉树遍历(前序、中序、后序、层次遍历、深度优先、广度优先)...
二叉树有多种遍历方法,有层次遍历、深度优先遍历、广度优先遍历等。
涉及到的代码都用Java编写。
1. 首先给出二叉树节点类
树节点:
class TreeNode{
int val;
//左子树
TreeNode left;
//右子树
TreeNode right;
//构造方法
TreeNode(int x) {
val = x;
}
}
无论是哪种遍历方法,考查节点的顺序都是一样的(思考做试卷的时候,人工遍历考查顺序)。只不过有时候考查了节点,将其暂存,需要之后的过程中输出。
如图所示,三种遍历方法(人工)得到的结果分别是:
先序:1246783 5
中序:4768213 5
后序:7864253 1
层次遍历:12345678
三种遍历方法的考查顺序一致,得到的结果却不一样,原因在于:
先序:考察到一个节点后,即刻输出该节点的值,并继续遍历其左右子树。(根左右)
中序:考察到一个节点后,将其暂存,遍历完左子树后,再输出该节点的值,然后遍历右子树。(左根右)
后序:考察到一个节点后,将其暂存,遍历完左右子树后,再输出该节点的值。(左右根)
层次遍历:只需按层次遍历即可
2. 先序遍历
2.1 递归先序遍历
递归先序遍历很容易理解,先输出节点的值,再递归遍历左右子树。中序和后序的递归类似,改变根节点输出位置即可。
// 递归先序遍历
public static void recursionPreorderTraversal(TreeNode root){
if (root != null) {
System.out.print(root.val + " ");
recursionPreorderTraversal(root.left);
recursionPreorderTraversal(root.right);
}
}
2.2 非递归先序遍历
因为要在遍历完节点的左子树后接着遍历节点的右子树,为了能找到该节点,需要使用栈来进行暂存。中序和后序也都涉及到回溯,所以都需要用到栈。
遍历过程参考注释
// 非递归先序遍历
public static void preorderTraversal(TreeNode root) {
// 用来暂存节点的栈
Stack treeNodeStack = new Stack();
// 新建一个游标节点为根节点
TreeNode node = root;
// 当遍历到最后一个节点的时候,无论它的左右子树都为空,并且栈也为空
// 所以,只要不同时满足这两点,都需要进入循环
while (node != null || !treeNodeStack.isEmpty()) {
// 若当前考查节点非空,则输出该节点的值
// 由考查顺序得知,需要一直往左走
while (node != null) {
System.out.print(node.val + " ");
// 为了之后能找到该节点的右子树,暂存该节点
treeNodeStack.push(node);
node = node.left;
}
// 一直到左子树为空,则开始考虑右子树
// 如果栈已空,就不需要再考虑
// 弹出栈顶元素,将游标等于该节点的右子树
if (!treeNodeStack.isEmpty()) {
node = treeNodeStack.pop();
node = node.right;
}
}
}
另外一种好理解
public void preOrderTraverse2(TreeNode root) {
LinkedList stack = new LinkedList<>();
TreeNode pNode = root;
while (pNode != null || !stack.isEmpty()) {
if (pNode != null) {
System.out.print(pNode.val+" ");
stack.push(pNode);
pNode = pNode.left;
} else { //pNode == null && !stack.isEmpty()
TreeNode node = stack.pop();
pNode = node.right;
}
}
}
2.3 先序遍历结果递归先序遍历: 1 2 4 6 7 8 3 5
非递归先序遍历:1 2 4 6 7 8 3 5
3. 中序遍历
3.1 递归中序遍历
过程和递归先序遍历类似
// 递归中序遍历
public static void recursionMiddleorderTraversal(TreeNode root){
if (root != null) {
recursionMiddleorderTraversal(root.left);
System.out.print(root.val + " ");
recursionMiddleorderTraversal(root.right);
}
}
3.2 非递归中序遍历
和非递归先序遍历类似,唯一区别是考查到当前节点时,并不直接输出该节点。
而是当考查节点为空时,从栈中弹出的时候再进行输出(永远先考虑左子树,直到左子树为空才访问根节点)。
// 非递归中序遍历
public static void middleorderTraversal(TreeNode root) {
Stack treeNodeStack = new Stack();
TreeNode node = root;
while (node != null || !treeNodeStack.isEmpty()) {
while (node != null) {
treeNodeStack.push(node);
node = node.left;
}
if (!treeNodeStack.isEmpty()) {
node = treeNodeStack.pop();
System.out.print(node.val + " ");
node = node.right;
}
}
}
另外一种好理解
public void inOrderTraverse2(TreeNode root) {
LinkedList stack = new LinkedList<>();
TreeNode pNode = root;
while (pNode != null || !stack.isEmpty()) {
if (pNode != null) {
stack.push(pNode);
pNode = pNode.left;
} else { //pNode == null && !stack.isEmpty()
TreeNode node = stack.pop();
System.out.print(node.val+" ");
pNode = node.right;
}
}
}
3.3 中序遍历结果递归中序遍历: 4 7 6 8 2 1 3 5
非递归中序遍历:4 7 6 8 2 1 3 5
4. 后序遍历
4.1 递归后序遍历
过程和递归先序遍历类似
// 递归后序遍历
public static void recursionPostorderTraversal(TreeNode root){
if (root != null) {
recursionPostorderTraversal(root.left);
recursionPostorderTraversal(root.right);
System.out.print(root.val + " ");
}
}
4.2 非递归后序遍历
后续遍历和先序、中序遍历不太一样。
后序遍历在决定是否可以输出当前节点的值的时候,需要考虑其左右子树是否都已经遍历完成。
所以需要设置一个lastVisit游标。
若lastVisit等于当前考查节点的右子树,表示该节点的左右子树都已经遍历完成,则可以输出当前节点。
并把lastVisit节点设置成当前节点,将当前游标节点node设置为空,下一轮就可以访问栈顶元素。
否者,需要接着考虑右子树,node = node.right。
以下考虑后序遍历中的三种情况:
如图所示,从节点1开始考查直到节点4的左子树为空。
注:此时的游标节点node = 4.left == null。
此时需要从栈中查看 Peek()栈顶元素。
发现节点4的右子树非空,需要接着考查右子树,4不能输出,node = node.right。
如图所示,考查到节点7(7.left == null,7是从栈中弹出),其左右子树都为空,可以直接输出7。
此时需要把lastVisit设置成节点7,并把游标节点node设置成null,下一轮循环的时候会考查栈中的节点6。
如图所示,考查完节点8之后(lastVisit == 节点8),将游标节点node赋值为栈顶元素6,节点6的右子树正好等于节点8。表示节点6的左右子树都已经遍历完成,直接输出6。
此时,可以将节点直接从栈中弹出Pop(),之前用的只是Peek()。
将游标节点node设置成null。
// 非递归后序遍历
public static void postorderTraversal(TreeNode root) {
Stack treeNodeStack = new Stack();
TreeNode node = root;
TreeNode lastVisit = root;
while (node != null || !treeNodeStack.isEmpty()) {
while (node != null) {
treeNodeStack.push(node);
node = node.left;
}
//查看当前栈顶元素
node = treeNodeStack.peek();
//如果其右子树也为空,或者右子树已经访问
//则可以直接输出当前节点的值
if (node.right == null || node.right == lastVisit) {
System.out.print(node.val + " ");
treeNodeStack.pop();
lastVisit = node;
node = null;
} else {
//否则,继续遍历右子树
node = node.right;
}
}
}
4.3 后序遍历结果递归后序遍历: 7 8 6 4 2 5 3 1
非递归后序遍历:7 8 6 4 2 5 3 1
5. 层级遍历
层次遍历的代码比较简单,只需要一个队列即可,先在队列中加入根结点。之后对于任意一个结点来说,在其出队列的时候,访问之。同时如果左孩子和右孩子有不为空的,入队列。代码如下:
public void levelTraverse(TreeNode root) {
if (root == null) {
return;
}
LinkedList queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
System.out.print(node.val+" ");
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
}
6. 深度优先遍历(DFS)
其实深度遍历就是上面的先序、中序和后序。但是为了保证与广度优先遍历相照应,也写在这。代码也比较好理解,其实就是先序遍历,代码如下:
前序遍历是深度优先遍历的一种。
但二叉树深度优先遍历还包括中序遍历、后续遍历。
数据结构:栈
public void depthOrderTraverse(TreeNode root) {
if (root == null) {
return;
}
LinkedList stack = new LinkedList<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
System.out.print(node.val+" ");
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
}
7. 广度优先遍历(BFS)
数据结构:队列
父节点入队,父节点出队列,先左子节点入队,后右子节点入队。递归遍历全部节点即可
public void levelOrderTraversal(){
if(root==null){
System.out.println("empty tree");
return;
}
ArrayDeque queue=new ArrayDeque();
queue.add(root);
while(queue.isEmpty()==false){
TreeNode node=queue.remove();
System.out.print(node.value+" ");
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
System.out.print("\n");
}
二叉树前序中序后序javascript_详解二叉树遍历(前序、中序、后序、层次遍历、深度优先、广度优先)...相关推荐
- Python中的select、epoll详解
Python中的select.epoll详解 文章目录 Python中的select.epoll详解 一.select 1.相关概念 2.select的特性 1.那么单进程是如何实现多并发的呢??? ...
- CSS3中font-face属性的用法详解
CSS3中font-face属性的用法详解 @font-face是CSS3中的一个模块,主要是把自定义的Web字体嵌入到你的网页中,随着@font-face模块的出现,我们在Web的开发中使用字体不怕 ...
- Python中数组切片的用法详解
Python中数组切片的用法详解 一.python中"::-1"代表什么? 二.python中":"的用法 三.python中数组切片 三.numpy中的整数数 ...
- Android中联系人和通话记录详解(2)
在文章Android中联系人和通话记录详解(1)中对通话记录进行了分析,本章将对联系人的数据库表.字段以及Insert,Query,Delelte,Update四大基本数据操作进行分析. 与联系人相关 ...
- 二叉树层次遍历算法 python_二叉树的遍历详解:前、中、后、层次遍历(Python实现)...
二叉树的遍历详解:前.中.后.层次遍历(Python实现) 二叉树是一种常见的数据结构,而它的常见遍历方法有前序遍历.中序遍历.后续遍历.层次遍历--掌握这几种遍历方法是很有必要的. 假设我们二叉树节 ...
- python多维数组添加元素_numpy中三维数组中加入元素后的位置详解
今天做数据处理时,遇到了从三维数组中批量加入二维数组的需求.其中三维数组在深度学习的特征数据处理时经常会使用到,所以读者有必要对该小知识点做到清楚了解并掌握.现对三维数组中的元素位置结合代码做详细归纳 ...
- python中返回上一步操作的代码_Pycharm代码跳转后退回操作详解
用Pycharm写Python代码有一段时间了,最近发现了一个Pycharm的一个小技巧想分享给大家,下面这篇文章主要给大家介绍了关于Pycharm代码跳转该如何回退的相关资料,文中介绍的非常详细,对 ...
- 详解二叉树的三种遍历方式(递归、迭代、Morris算法)
详解二叉树的三种遍历方式(递归.迭代.Morris算法) 最重要的事情写在前面:遍历顺序不一定就是操作顺序!!! 递归解法 首先,一颗二叉树它的递归序列是一定的,导致其前中后序不同的原因只不过是访问节 ...
- Oracle中序列(Sequence)详解
Oracle中序列(Sequence)详解 一 序列定义 序列(SEQUENCE)是序列号生成器,可以为表中的行自动生成序列号,产生一组等间隔的数值(类型为数字).不占用磁盘空间,占用内存. 其主要用 ...
- python的sort方法是哪种_python中的sort方法使用详解
Python中的sort()方法用于数组排序,本文以实例形式对此加以详细说明: 一.基本形式列表有自己的sort方法,其对列表进行原址排序,既然是原址排序,那显然元组不可能拥有这种方法,因为元组是不可 ...
最新文章
- 求两个矩形重叠部分的面积
- 让你的容器兼容STL
- 8086寄存器组织结构
- matlab 低秩矩阵分解,低秩分解的matlab代码看不懂,分解的两个矩阵在哪呀??...
- python socket发送数组_利用pyprocessing初步探索数组排序算法可视化
- NotificationManagerService使用详解与原理分析(一)
- 0.Boot Loader的概念和功能
- 全国计算机等级考试暨南大学,9月暨南大学计算机等级考试报名时间通知
- LeetCode 1618. 找出适应屏幕的最大字号(二分查找)
- python登录系统的实现方法_python实现简单登陆系统
- IDL | 语句练习
- Linux svn 命令每次都要输入密码o(╯□╰)o
- python打印N*N乘法表
- 201671010457 朱石景 实验四《英文文本词频统计分析》 结对项目报告
- 功能更新 | 身份认证增强安全配置
- ERROR 1062 (23000): ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '1'
- 【数据库】数据库索引
- 前端实现在线预览、编辑Office文档(vue版)
- 人生第一次面试还是霸面----cvte
- 附视频教程|如何优雅扩容云硬盘
热门文章
- [Curator] Leader Latch 的使用与分析
- 【Hadoop Summit Tokyo 2016】欢迎来到Hadoop的青春时代
- 今天博客园肿了吗?希望团队修复一下
- Windows2008之文件服务器资源管理器
- BZOJ2395 [Balkan 2011]Timeismoney 【最小乘积生成树】
- 王者荣耀交流协会final发布-第一次scrum立会
- Spring学习总结四——SpringIOC容器四
- (新年快乐)ABP理论学习之本地化(2016第一篇)
- 网站加速--Cache为王篇
- 处理顶点——使用顶点缓冲和索引缓冲将顶点和索引保存在显存中