二叉树的遍历(traversing binary tree)是指从根节点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。
本文介绍了4种二叉树的遍历方法,分别是前序、中序、后续、层序遍历,并且每种方法均提供了详尽的Java语言的代码演示,在最后还介绍了遍历结果推导的方法。
1 概述二叉树的遍历(traversing binary tree)是指从根节点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。
访问其实是要根据实际的需要来确定具体做什么,比如对每个结点进行相关计算,输出打印等,它算作是一个抽象操作。在这里我们可以简单地假定访问就是输出结点的数据信息。
二叉树的遍历次序不同于线性结构,最多也就是从头至尾、循环、双向等简单的遍历方式。树的结点之间不存在唯一的前驱和后继关系,在访问一个结点后,下一个被访问的结点面临着不同的选择。因此遍历的方式也有很多种,主要的方式有先序遍历、中序遍历、后序遍历、层序遍历。
关于二叉树的知识,如果不是很了解,可以看这篇文章:二叉树的入门以及Java实现案例详解。
本文将以下面的二叉树来作为样例进行遍历:
2 构建二叉树首先,构建出上图中二叉树,下面是二叉树的简单链式存储结构的实现。二叉树的实现在:二叉树的入门以及Java实现案例详解中有讲解。
/***二叉树的链式存储结构的简单实现*/publicclassLinkedBinaryTree<E>{/***外部保存根节点的引用*/privateBinaryTreeNode<E>root;/***树节点的数量*/privateintsize;/***内部节点对象**@param<E>数据类型*/publicstaticclassBinaryTreeNode<E>{//数据域Edata;//左子节点BinaryTreeNode<E>left;//右子节点BinaryTreeNode<E>right;publicBinaryTreeNode(Edata){this.data=data;}publicBinaryTreeNode(Edata,BinaryTreeNode<E>left,BinaryTreeNode<E>right){this.data=data;this.left=left;this.right=right;}@OverridepublicStringtoString(){returndata.toString();}/*@OverridepublicStringtoString(){return"BinaryTreeNode{"+"data="+data+'}';}*/}/***空构造器*/publicLinkedBinaryTree(){}/***构造器,初始化root节点**@paramroot根节点数据*/publicLinkedBinaryTree(Eroot){checkNullData(root);this.root=newBinaryTreeNode<>(root);size++;}/***添加子节点**@paramparent父节点的引用*@paramdata节点数据*@paramleft是否是左子节点,true是;false否*/publicBinaryTreeNode<E>addChild(BinaryTreeNode<E>parent,Edata,booleanleft){checkNullParent(parent);checkNullData(data);BinaryTreeNode<E>node=newBinaryTreeNode<>(data);if(left){if(parent.left!=null){thrownewIllegalStateException("该父节点已经存在左子节点,添加失败");}parent.left=node;}else{if(parent.right!=null){thrownewIllegalStateException("该父节点已经存在右子节点,添加失败");}parent.right=node;}size++;returnnode;}/***是否是空树**@returntrue是;false否*/publicbooleanisEmpty(){returnsize==0;}/***获取根节点**@return根节点;或者null--表示空树*/publicBinaryTreeNode<E>getRoot(){returnroot;}/***获取左子节点**@paramparent父节点引用*@return左子节点或者null--表示没有左子节点*/publicBinaryTreeNode<E>getLeft(BinaryTreeNode<E>parent){returnparent==null?null:parent.left;}/***获取右子节点**@paramparent父节点引用*@return右子节点或者null--表示没有右子节点*/publicBinaryTreeNode<E>getRight(BinaryTreeNode<E>parent){returnparent==null?null:parent.right;}/***数据判null**@paramdata添加的数据*/privatevoidcheckNullData(Edata){if(data==null){thrownewNullPointerException("数据不允许为null");}}/***检查父节点是否为null**@paramparent父节点引用*/privatevoidcheckNullParent(BinaryTreeNode<E>parent){if(parent==null){thrownewNoSuchElementException("父节点不能为null");}}}2.1 添加节点数据publicclassTreeTest{/***构建二叉树,添加根节点r*/LinkedBinaryTree<String>integerLinkedBinaryTree=newLinkedBinaryTree<>("r");@BeforepublicvoidbuildTree(){/*构建二叉树*/LinkedBinaryTree.BinaryTreeNode<String>r=integerLinkedBinaryTree.getRoot();//添加r根节点的左子结点aLinkedBinaryTree.BinaryTreeNode<String>a=integerLinkedBinaryTree.addChild(r,"a",true);//添加r根节点的右子结点bLinkedBinaryTree.BinaryTreeNode<String>b=integerLinkedBinaryTree.addChild(r,"b",false);//添加a节点的左子结点cLinkedBinaryTree.BinaryTreeNode<String>c=integerLinkedBinaryTree.addChild(a,"c",true);//添加a节点的右子结点dLinkedBinaryTree.BinaryTreeNode<String>d=integerLinkedBinaryTree.addChild(a,"d",false);//添加b节点的左子结点eLinkedBinaryTree.BinaryTreeNode<String>e=integerLinkedBinaryTree.addChild(b,"e",true);//添加b节点的右子结点fLinkedBinaryTree.BinaryTreeNode<String>f=integerLinkedBinaryTree.addChild(b,"f",false);//添加c节点的左子结点gLinkedBinaryTree.BinaryTreeNode<String>g=integerLinkedBinaryTree.addChild(c,"g",true);//添加c节点的右子结点hLinkedBinaryTree.BinaryTreeNode<String>h=integerLinkedBinaryTree.addChild(c,"h",false);//添加d节点的左子结点iLinkedBinaryTree.BinaryTreeNode<String>i=integerLinkedBinaryTree.addChild(d,"i",true);//添加f节点的左子结点jLinkedBinaryTree.BinaryTreeNode<String>j=integerLinkedBinaryTree.addChild(f,"j",true);}}3 先序遍历 preorder traversal3.1 先序遍历的介绍规则是:若二叉树为空,则空操作返回;否则,从根节点开始,先遍历根,然后是左子树,最后遍历右子树;对于子树,同样从子树根开始,先遍历根,然后是左子树,最后遍历右子树。采用了递归的思想。
先序遍历中,对节点的遍历工作,是在对该节点的儿子节点的遍历之前进行遍历的。这也就是“先序”的得来。
先序遍历也叫前序遍历。
从r根节点开始,其遍历流程如下:
3.2 先序遍历的简单实现以下代码添加到LinkedBinaryTree类中。
/***保存遍历出来的节点数据*/ThreadLocal<StringBuilder>threadLocal=ThreadLocal.withInitial(StringBuilder::new);/***先序遍历,提供给外部使用的api**@return遍历的数据*/publicStringtoPreorderTraversalString(){//如果是空树,直接返回空if(isEmpty()){returnnull;}//从根节点开始递归preorderTraversal(root);//获取遍历结果Strings1=threadLocal.get().toString();threadLocal.remove();returns1.substring(0,s1.length()-3);}/***先序遍历内部使用的递归遍历方法**@paramroot节点,从根节点开始*/privatevoidpreorderTraversal(BinaryTreeNode<E>root){//添加数节点threadLocal.get().append(root).append("-->");//获取节点的左子节点BinaryTreeNode<E>left=getLeft(root);if(left!=null){//如果左子节点不为null,则继续递归遍历该左子节点preorderTraversal(left);}//获取节点的右子节点BinaryTreeNode<E>right=getRight(root);if(right!=null){//如果右子节点不为null,则继续递归遍历该右子节点preorderTraversal(right);}}3.2.1 测试/***先序遍历*/@TestpublicvoidpreorderTraversal(){Strings=integerLinkedBinaryTree.toPreorderTraversalString();System.out.println(s);}查看输出:
r-->a-->c-->g-->h-->d-->i-->b-->e-->f-->j
确实图中的遍历顺序一致。
4 中序遍历 inorder traversal4.1 中序遍历的介绍规则是:若树为空,则空操作返回;否则,从根节点开始,先遍历根结点的左子树,然后是访问根结点,最后遍历右子树;对于子树,同样,先遍历子树根节点的左子树,然后是访问子树根结点,最后遍历右子树。采用了递归的思想。
中序遍历中,对节点的遍历工作,是在对该节点的左儿子节点的遍历之后、右儿子节点的遍历之前进行遍历的。这也就是“中序”的得来。
对图中的树采用中序遍历,从r根节点开始,顺序如下:
4.2 中序遍历的简单实现/***中序遍历,提供给外部使用的api**@return遍历的数据*/publicStringtoInorderTraversalString(){//如果是空树,直接返回空if(isEmpty()){returnnull;}//从根节点开始递归inorderTraversal(root);//获取遍历结果Strings1=threadLocal.get().toString();threadLocal.remove();returns1.substring(0,s1.length()-3);}/***中序遍历内部使用的递归遍历方法**@paramroot节点,从根节点开始*/privatevoidinorderTraversal(BinaryTreeNode<E>root){BinaryTreeNode<E>left=getLeft(root);if(left!=null){//如果左子节点不为null,则继续递归遍历该左子节点inorderTraversal(left);}//添加数据节点threadLocal.get().append(root).append("-->");//获取节点的右子节点BinaryTreeNode<E>right=getRight(root);if(right!=null){//如果右子节点不为null,则继续递归遍历该右子节点inorderTraversal(right);}}4.2.1 测试/***中序遍历*/@TestpublicvoidinorderTraversal(){Strings=integerLinkedBinaryTree.toInorderTraversalString();System.out.println(s);}查看输出:
g-->c-->h-->a-->i-->d-->r-->e-->b-->j-->f
确实图中的遍历顺序一致。
5 后序遍历 postorder traversal5.1 后序遍历的介绍规则是:若树为空,则空操作返回;否则,从根节点开始,先遍历根结点的左子树,然后是遍历右子树,最后遍历根结点;对于子树,同样,先遍历子树根节点的左子树,然后是遍历右子树,最后遍历子树根结点。采用了递归的思想。
后序遍历中,对节点的遍历工作,是在对该节点的儿子节点的遍历之后进行遍历的。这也就是“后序”的得来。
对图中的树采用后序遍历,从r根节点开始,顺序如下:
5.2 后序遍历的简单实现/***后序遍历,提供给外部使用的api**@return遍历的数据*/publicStringtoPostorderTraversalString(){//如果是空树,直接返回空if(isEmpty()){returnnull;}//从根节点开始递归postorderTraversal(root);//获取遍历结果Strings1=threadLocal.get().toString();threadLocal.remove();returns1.substring(0,s1.length()-3);}/***后序遍历内部使用的递归遍历方法**@paramroot节点,从根节点开始*/privatevoidpostorderTraversal(BinaryTreeNode<E>root){BinaryTreeNode<E>left=getLeft(root);if(left!=null){//如果左子节点不为null,则继续递归遍历该左子节点postorderTraversal(left);}//获取节点的右子节点BinaryTreeNode<E>right=getRight(root);if(right!=null){//如果右子节点不为null,则继续递归遍历该右子节点postorderTraversal(right);}//添加数据节点threadLocal.get().append(root).append("-->");}5.2.1 测试/***后序遍历*/@TestpublicvoidpostorderTraversal(){Strings=integerLinkedBinaryTree.toPostorderTraversalString();System.out.println(s);}查看输出:
g-->h-->c-->i-->d-->a-->e-->j-->f-->b-->r
确实图中的遍历顺序一致。
6 层序遍历 level traversal6.1 层序遍历的介绍规则是:若树为空,则空操作返回;否则从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问。 层序遍历中,对节点的遍历工作,上层节点的遍历先于下层节点的遍历。这也就是“层序”的得来。
对图中的树采用层序遍历,从r根节点开始,顺序如下:
6.2 层序遍历的简单实现/***层序遍历,提供给外部使用的api**@return遍历的数据*/publicStringtoLevelTraversalString(){//如果是空树,直接返回空if(isEmpty()){returnnull;}//从根节点开始遍历,借用队列levelTraversal(root);//获取遍历结果Strings1=threadLocal.get().toString();threadLocal.remove();returns1.substring(0,s1.length()-3);}/***层序遍历内部使用的借用了队列**@paramroot节点,从根节点开始*/privatevoidlevelTraversal(BinaryTreeNode<E>root){Queue<BinaryTreeNode<E>>q=newLinkedList<>();q.add(root);while(!q.isEmpty()){BinaryTreeNode<E>nowNode=q.poll();//添加数据节点threadLocal.get().append(nowNode.data).append("-->");if(nowNode.left!=null){//如果左子节点不为null,则将子节点加入队列q.add(nowNode.left);}if(nowNode.right!=null){//如果右子节点不为null,则将子节点加入队列q.add(nowNode.right);}}}6.2.1 测试/***层序遍历*/@TestpublicvoidlevelTraversal(){Strings=integerLinkedBinaryTree.toLevelTraversalString();System.out.println(s);}查看输出:
r-->a-->b-->c-->d-->e-->f-->g-->h-->i-->j
确实图中的遍历顺序一致。
7 遍历结果推导题目:已知一棵二叉树的前序遍历序列为r-->a-->c-->h-->d-->i-->b-->e-->j-->k-->f,中序遍历序列为c-->h-->a-->d-->i-->r-->e-->k-->j-->b-->f,请问这棵二叉树的后序遍历结果是多少?
这样的题目常出现在面试中,这个原理其实并不是很复杂,并且有一定的规律性。下面一起来推导。
7.1 寻找根节点找到前序遍历的第1个字符:r,可知r是根节点;
再找到中序遍历中的根节点r的位置,r的左边c-->h-->a-->d-->i,这就是最大的左子树Ⅰ,e-->k-->j-->b-->f就是最大的右子树Ⅱ。
7.2 推导左子树找到前序遍历的第2个字符:a,然后在中序遍历的子树Ⅰ中也能找到节点a,可知a是r根节点的左子节点,也是左子树Ⅰ的根节点,;那么可以推导出来,左子树Ⅰ还具有左子树Ⅲ:c-->h和右子树Ⅳ:d-->i
找到前序遍历的第3、4个字符:c-->h,可知,c作为子树Ⅰ的左子结点,对比中顺序遍历顺序c-->h,可知h是c节点的右子结点;
找到前序遍历的第5、6个字符:d-->i,可知,d作为子树Ⅰ的右子结点,对比中顺序遍历顺序d-->i,可知 i是d节点的右子节点;
到此可以推导出最大的左子树的数据结构如下:
下面推导右子树。
7.3 推导右子树右子树Ⅱ的前序遍历节点就是排除左子树和根节点剩下的节点:b-->e-->j-->k-->f,中序遍历为e-->k-->j-->b-->f
前序遍历的第一个节点是b,可以b是右子树Ⅱ的根节点;
在中序遍历中b节点的左边是e-->k-->j,右边是f。可以f是节点b的右子结点,并且是叶子节点;这样还剩下最后的左子树Ⅴ;<
logo设计
创造品牌价值
¥500元起
APP开发
量身定制,源码交付
¥2000元起
商标注册
一个好品牌从商标开始
¥1480元起
公司注册
注册公司全程代办
¥0元起
查
看
更
多
数据结构—二叉树的4种遍历方式详解以及Java代码的完整演示
首先,构建出上图中二叉树,下面是二叉树的简单链式存储结构的实现。二叉树的实现在:二叉树的入门以及Java实现案例详解中有讲解。\/***二叉树的链式存储结构的简单实现*\/publicclassLinkedBinaryTree<E>{\/***外部保存根节点的引用*\/privateBinaryTreeNode<E>root;\/***树节点的数量*\/privateintsize;\/***内部节点对...
【数据结构】遍历二叉树
后序遍历:先遍历左右子树,最后访问根结点,如GHDBIEFCA。层序遍历:从根开始,逐层向下,同一层从左到右,如ABCDEFGHI。通过前序、中序和后序遍历,我们可以把二叉树的结点序列化为线性结构,便于编程处理。例如,给定前序和中序遍历序列推导后序遍历,如前序为ABCDEF,中序为CBAEDF,通过分析可以推...
数据结构二叉树遍历方式学生收藏
数据结构计算机专业必学知识二叉树的遍历 先序遍历 先序遍历可以想象为,一个小人从一棵二叉树根节点为起点,沿着二叉树外沿,逆时针走一圈回到根节点,路上遇到的元素顺序,就是先序遍历的结果。巧记:根左右 先序遍历结果为:ABD HI EJCFKG 中序遍历 中序遍历可以看成,二叉树每个节点,垂直方向投影下来(可以理解为...
数据结构:二叉树的基础概念和算法
二叉树的遍历方式有四种:前序遍历(根左右)、中序遍历(左根右)、后序遍历(左右根)和层序遍历(从上至下)。熟练掌握二叉树的遍历,包括递归和非递归实现方法的三种前序、中序和后序遍历。宽度优先遍历适用于二叉树的层次遍历,从上到下,每层结点从左到右依次访问。二叉树的特例包括二叉搜索树...
数据结构中的二叉树
具体实现包括定义节点结构、遍历函数和主函数等。通过递归方法求解二叉树深度。赫夫曼树(Huffman Tree)是带权路径长度最短的树,应用于数据压缩等领域。赫夫曼树的构建方法如下:(1)每次从最小的两个结点中取出,将较小的放在左边,较大与之相加。(2)重复步骤(1),直至所有结点合并成一个树。...
请教一下数据结构 二叉树的先序遍历 中序遍历 后序遍历 是怎么弄的
后序遍历算法:(1) 后序遍历根结点的左子树;(2) 后序遍历根结点的右子树。(3) 访问二叉树的根结点;你的方法是将树分解为根、左子树、右子树,再将子树继续按前述方法分解,直至每一部分只剩一个结点或空为止。对该图,分解为 根(a),根的左子树(bde,不分先后),根的右子树(cf,不分...
数据结构—树、森林和二叉树的转换详解
上面的树,它的先根遍历序列为radijkebfcgh,后根遍历序列为ijkdeafbghcr。先根遍历、后根遍历的顺序实际上和该树转换为二叉树后的先序遍历、中序遍历是一致的。关于二叉树的遍历,可以看这篇文章:二叉树的4种遍历方式详解以及Java代码的完整演示。5.2 森林的遍历森林的遍历也分为两种方式:前序...
数据结构二叉树前序、中序、后续?
6,可知8为根节点6的左子树 因此该子树根节点为6,左子树为8,无右子树 如果按你说的右为8,那么其中序遍历应为6 8而不是8 6 总之先通过前序遍历可以确定根节点,再通过中序遍历才能确定左右子树 一定要两者结合才能得到二叉树的完整结构,不能只看其中之一 码字不易,望采纳~
二叉树的遍历规律是?
遍历规律52143算法如下:5-2=3;5-1=4;5-4=1;5-3=2;1+4=5;2+3=5;4-3=1;1+3=4。在计算机科学中,所谓遍历(Traversal),是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问题。遍历序列是指沿着某条搜索路线访问序列中的元素...
最全二叉树:完整详解二叉树的遍历以及完全二叉树等6种二叉树
例如,在一棵二叉树中,如果我们采用先序遍历,那么遍历顺序为:ABDFECGHI。而中序遍历(左根右)的结果则为:DBEFAGHCI。后序遍历(左右根)的结果则是:DEFBHGICA。在探讨完二叉树的遍历方式后,我们继续深入二叉树的种类。这里有几种基本的二叉树类型:满二叉树、完全二叉树、二叉查找树(BST)与...