【树】二叉树遍历算法(深度优先、广度优先遍历,前序、中序、后序、层次)及Java实现

目录

  • 一、前序遍历
  • 二、中序遍历
  • 三、后序遍历
  • 四、层次遍历
  • 遍历的作用

二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的。对于二叉树,有深度遍历和广度遍历,深度遍历有前序、中序以及后序三种遍历方法,广度遍历即我们平常所说的层次遍历。因为树的定义本身就是递归定义,因此采用递归的方法去实现树的三种遍历不仅容易理解而且代码很简洁,而对于广度遍历来说,需要其他数据结构的支撑,比如堆了。所以,对于一段代码来说,可读性有时候要比代码本身的效率要重要的多。

四种主要的遍历思想为:

前序遍历:根结点 ---> 左子树 ---> 右子树

中序遍历:左子树---> 根结点 ---> 右子树

后序遍历:左子树 ---> 右子树 ---> 根结点

层次遍历:只需按层次遍历即可

例如,求下面二叉树的各种遍历

前序遍历:1 2 4 5 7 8 3 6

中序遍历:4 2 7 5 8 1 3 6

后序遍历:4 7 8 5 2 6 3 1

层次遍历:1 2 3 4 5 6 7 8

一、前序遍历

1)根据上文提到的遍历思路:根结点 ---> 左子树 ---> 右子树,很容易写出递归版本:

public void preOrderTraverse1(TreeNode root) {  if (root != null) {  System.out.print(root.val+"  ");  preOrderTraverse1(root.left);  preOrderTraverse1(root.right);  }
}  

2)现在讨论非递归的版本:

根据前序遍历的顺序,优先访问根结点,然后在访问左子树和右子树。所以,对于任意结点node,第一部分即直接访问之,之后在判断左子树是否为空,不为空时即重复上面的步骤,直到其为空。若为空,则需要访问右子树。注意,在访问过左孩子之后,需要反过来访问其右孩子,所以,需要栈这种数据结构的支持。对于任意一个结点node,具体步骤如下:

a)访问之,并把结点node入栈,当前结点置为左孩子;

b)判断结点node是否为空,若为空,则取出栈顶结点并出栈,将右孩子置为当前结点;否则重复a)步直到当前结点为空或者栈为空(可以发现栈中的结点就是为了访问右孩子才存储的)

代码如下:

public void preOrderTraverse2(TreeNode root) {  LinkedList<TreeNode> 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;  }  }
} 

二、中序遍历

1)根据上文提到的遍历思路:左子树 ---> 根结点 ---> 右子树,很容易写出递归版本:

public void inOrderTraverse1(TreeNode root) {  if (root != null) {  inOrderTraverse1(root.left);  System.out.print(root.val+"  ");  inOrderTraverse1(root.right);}
}  

2)非递归实现,有了上面前序的解释,中序也就比较简单了,相同的道理。只不过访问的顺序移到出栈时。代码如下:

public void inOrderTraverse2(TreeNode root) {  LinkedList<TreeNode> 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;  }  }
}  

三、后序遍历

1)根据上文提到的遍历思路:左子树 ---> 右子树 ---> 根结点,很容易写出递归版本:

public void postOrderTraverse1(TreeNode root) {  if (root != null) {  postOrderTraverse1(root.left);  postOrderTraverse1(root.right);  System.out.print(root.val+"  ");  }  }  

2)非递归的代码,lastVisited这个结点指针解释:
根结点先进入stack栈,然后左子结点或右子结点,特别是右子结点弹出来之后,再弹出根结点,这个lastVisited这个结点指针就指示右子结点已经被访问完了,可以弹出根结点了。

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode(int x) { val = x; }* }*/
public class Solution {public List<Integer> postorderTraversal(TreeNode root) {Stack<TreeNode> stack = new Stack<>();List<Integer> list = new LinkedList<>();TreeNode lastVisited = null;while(!stack.isEmpty() || root != null) {if (root != null) {stack.push(root);root = root.left;} else {TreeNode node = stack.peek();if (node.right != null && lastVisited != node.right) {root = node.right;} else {list.add(node.val);lastVisited = stack.pop();}}}return list;}
}

四、层次遍历

层次遍历的代码比较简单,只需要一个队列即可,先在队列中加入根结点。之后对于任意一个结点来说,在其出队列的时候,访问之。同时如果左孩子和右孩子有不为空的,入队列。代码如下:

public void levelTraverse(TreeNode root) {  if (root == null) {  return;  }  LinkedList<TreeNode> 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);  }  }
}  

五、深度优先遍历

其实深度遍历就是上面的前序、中序和后序。但是为了保证与广度优先遍历相照应,也写在这。代码也比较好理解,其实就是前序遍历,代码如下:

public void depthOrderTraverse(TreeNode root) {  if (root == null) {  return;  }  LinkedList<TreeNode> 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);  }  }
} 

遍历的作用

先序遍历:在第一次遍历到节点时就执行操作,一般只是想遍历执行操作(或输出结果)可选用先序遍历;中序遍历:对于二分搜索树,中序遍历的操作顺序(或输出结果顺序)是符合从小到大(或从大到小)顺序的,故要遍历输出排序好的结果需要使用中序遍历后序遍历:后续遍历的特点是执行操作时,肯定已经遍历过该节点的左右子节点,故适用于要进行破坏性操作的情况,比如删除所有节点

来源:

http://blog.csdn.net/my_jobs/article/details/43451187
二叉树后序遍历的非递归实现:http://bookshadow.com/weblog/2015/01/19/binary-tree-post-order-traversal/

posted @ 2018-03-08 16:33 ToOnE 阅读(...) 评论(...) 编辑 收藏

【树】二叉树遍历算法(深度优先、广度优先遍历,前序、中序、后序、层次)及Java实现...相关推荐

  1. 二叉树层次遍历算法 python_二叉树的遍历详解:前、中、后、层次遍历(Python实现)...

    二叉树的遍历详解:前.中.后.层次遍历(Python实现) 二叉树是一种常见的数据结构,而它的常见遍历方法有前序遍历.中序遍历.后续遍历.层次遍历--掌握这几种遍历方法是很有必要的. 假设我们二叉树节 ...

  2. 数据结构与算法实验 实验6:二叉树ADT的二叉链式实现 (由完全前序序列创建二叉树 / 求二叉树的节点数/树高/叶子节点数 /先序中序后序层序遍历)

    假设二叉数的数据元素为字符,采用二叉链式存储结构.请编码实现二叉树ADT,其中包括创建二叉树.遍历二叉树(深度.广度).求二叉树的深度(高度).计算二叉树的元素个数.计算二叉树的叶子数.二叉树的格式输 ...

  3. 数据结构与算法(7-2)图的遍历(深度优先遍历DFS、广度优先遍历BFS)(分别用邻接矩阵和邻接表实现)

    目录 深度优先遍历(DFS)和广度优先遍历(BFS)原理 1.自己的原理图 2.官方原理图 一.邻接矩阵的深度优先遍历(DFS) 1.原理图 2. 过程: 3.总代码 二.邻接表的深度优先遍历(DFS ...

  4. 二叉树前序中序后续线索树_二叉树的先序,中序,后序遍历以及线索二叉树的遍历...

    二叉树的先序,中序,后序遍历以及线索二叉树的遍历 (2008-05-04 17:52:49) 标签: 杂谈 C++ 二叉树的先序,中序,后序遍历以及线索二叉树的遍历 头文件 //*********** ...

  5. 【数据结构笔记10】二叉树的先序、中序、后序遍历,中序遍历的堆栈/非递归遍历算法,层序遍历,确定一个二叉树,树的同构

    本次笔记内容: 3.3.1 先序中序后序遍历 3.3.2 中序非递归遍历 3.3.3 层序遍历 3.3.4 遍历应用例子 小白专场:题意理解及二叉树表示 小白专场:程序框架.建树及同构判别 文章目录 ...

  6. java 树 广度优先遍历_Java二进制搜索树遍历操作的详细描述[前,中,后,层次,广度优先遍历]...

    本文介绍了Java二进制搜索树遍历操作. 与您分享以供参考,如下: 前言: 在Java Binary Search Tree Basics的上一节中,我们了解了该树及其相关知识,并对Binary Se ...

  7. (数据结构)1.实现二叉树的各种基本运算的算法2.实现二叉树的各种遍历算法3. 由遍历序列构造二叉树4.求二叉树中的结点个数、叶子节点个数、某结点层次和二叉树宽度

    实验内容 1.假设二叉树中的每个结点值为单个字符,采用二叉链存储结构存储.设计一个算法,计算一棵给定二叉树b中的所有单分支结点个数. 2.假设二叉树中的每个结点值为单个字符,采用二叉链存储结构存储.设 ...

  8. 树/二叉树/森林之间的相互转换 与遍历

    森林就是多棵树的集合,但是森林也可以只有一棵树,二叉树是一种特殊的树,固定的度为2,这是基本前情提要- 树常见的存储方式有三种: (1)双亲表示法 仅用定义一个结点对象,一个数组,代码定义如下: ty ...

  9. 【二叉树Java】二叉树遍历前序中序后序遍历的非递归写法

    本文主要介绍二叉树前序中序后序遍历的非递归写法 在探讨如何写出二叉树的前序中序后序遍历代码之前,我们先来明确一个问题,前序中序后序遍历根据什么区分? 二叉树的前序中序后序遍历,是相较根节点说的.最先遍 ...

  10. 二叉树的前序中序后序遍历java代码实现

    1.前序遍历概述 前序遍历(VLR) 是二叉树遍历的一种,也叫做先根遍历.先序遍历.前序周游,可记做根左右.前序遍历首先访问根结点然后遍历左子树,最后遍历右子树. 若二叉树为空则结束返回,否则: (1 ...

最新文章

  1. 习题4-3 求分数序列前N项和 (15 分)
  2. 关于C#开发山寨操作系统,程序语言,浏览器,IDE,Office,Photoshop等大型程序的可行性歪论及意义...
  3. django ajax文件上传,django 之 ajax 篇 上传文件
  4. 【历史上的今天】8 月 13 日:Oracle 起诉 Google,Java 版权案正式开启!
  5. Leetcode 1013. 总持续时间可被 60 整除的歌曲
  6. ERP系统多少钱一套?不同情况详情分析告诉你!
  7. 学计算机专业需要买电脑吗高中,麻麻 上大学专业需要用 | 买笔记本电脑_笔记本新闻-中关村在线...
  8. IDEA工具-鼠标滚轮调整字体大小
  9. 苹果计算机音频无法使用,解决Mac电脑直播没有电脑内声音的问题
  10. 衣带渐宽终不悔,为UI消得人憔悴
  11. tomcat修改主页
  12. 光猫超级管理员账号密码和Telnet登陆
  13. 自己实现notifyDatasetChanged
  14. 2022年高教社杯国赛E题思路——小批量物料的生产安排
  15. 没解锁的一加手机刷Recovery的方法
  16. 实现BPS组织机构数据权限分离解决方案
  17. (转)top关键字与top表达式(SQLServer)
  18. C++语言程序设计第五版 - 郑莉(第九章课后习题)
  19. centos7搭建DNS服务,CA字签证书
  20. 直流电流传感器行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)

热门文章

  1. 关于网站模板的下载地址
  2. GPIO模拟红外发射
  3. Linux RedHat 安装Perl环境
  4. 诺基亚n79 java性能_诺基亚N79即将出现,N79与N82对比,综合性能与可买度提升大吗?...
  5. IBM公司新款处理器宣称将提供新的数据加密级别
  6. AFL(American Fuzzy Lop)源码详细解读(2)
  7. 诗歌(9)—题西林壁
  8. 我终于不得不对MATLAB下手了
  9. 将UBB代码转换成html代码 转
  10. 登录页面的SQL注入漏洞