二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。根据定义中的某种次序,二叉树的遍历方式主要分为前序遍历,中序遍历,后序遍历以及层序遍历。
前序遍历:若树为空空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。
中序遍历:若树为空空操作返回,否则从树的根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后访问根结点,最后中序遍历右子树。
后序遍历:若树为空空操作返回,否则从树的根结点开始,后序遍历根结点的左子树,再后序遍历根结点的右子树,最后访问根结点。
1.前序遍历可以简单记为(根左右),中序遍历(左根右),后序遍历(左右根),哪个序遍历根就在哪个位置。从遍历方法的定义中可以看出,遍历的实现算法也可以采用递归。
实现代码:
package com.java.Search; /* * 二叉树的前序,中序,后序遍历 * 以二叉排序树的特点创建一棵树 * */ public class SearchTest { public static void main(String[] args){ BiTree bt=new BiTree(); BiTree.insert(bt, 2); BiTree.insert(bt, 8); BiTree.insert(bt, 7); BiTree.insert(bt, 4); BiTree.insert(bt, 9); BiTree.insert(bt, 3); BiTree.insert(bt, 1); BiTree.insert(bt, 6); BiTree.insert(bt, 7); BiTree.insert(bt, 5); System.out.println(bt); System.out.print("前序遍历:"); preorderTreverse(bt.root); System.out.print("中序遍历:"); mediumTreverse(bt.root); System.out.print("后序遍历:"); postorderTreverse(bt.root); } //前序遍历(根左右) public static void preorderTreverse(BiNode bt){ if(bt==null){ System.out.print(""); }else{ //访问根结点(访问定义为打印结点数值) System.out.print(bt.data); //前序遍历访问根结点的左子树 preorderTreverse(bt.lchild); //前序遍历访问根结点的右子树 preorderTreverse(bt.rchild); } } //中序遍历(左根右) public static void mediumTreverse(BiNode bt){ if(bt==null){ System.out.print(""); }else{ //中序遍历访问根结点的左子树 mediumTreverse(bt.lchild); //访问根结点 System.out.print(bt.data); //中序遍历访问根结点的右子树 mediumTreverse(bt.rchild); } } //后序遍历(左右根) public static void postorderTreverse(BiNode bt){ if(bt==null){ System.out.print(""); }else{ //后序遍历根结点的左子树 postorderTreverse(bt.lchild); //后序遍历根结点的右子树 postorderTreverse(bt.rchild); //访问根结点 System.out.print(bt.data); } } }
上述代码中创建一棵二叉树是在前面二叉排序树的基础上创建的,也就是创建的这棵树满足二叉排序树的特点。然后对二叉排序树进行前序遍历,中序遍历以及后序遍历。
以前序遍历为例,首先判断是否为空,若为空,输出空字符串,否则先访问根结点,打印输出根结点值2,然后前序遍历访问根结点(设为root)的左子树,即调用preorderTreverse(root.lchild),传入的参数为根结点的左子树,同样开始执行preorderTreverse()的函数流程,先打印输出左子树的根结点值1,然后前序遍历值为1的结点(设为p)的左子树,即递归调用preorderTreverse(p.lchild),为空输出空字符串,然后调用preorderTreverse(p.rchild),也为空输出空字符串,到此访问root.lchild的调用结束,回到函数主体,调用preorderTreverse(root.rchild),传入的参数为根结点的右子树,开始右子树的递归调用。
2.层序遍历
层序遍历规则是若树为空,按空操作返回,否则从树的第一层(根结点)开始访问,从上而下逐层遍历,在同一层中,按照从左到右的顺序对结点逐个访问。
实现思路:
- 首先判断根结点是否为空,若空,返回null;
- 若非空,将根结点放入链表中;
- 若链表非空,每次都检索并删除此链表的头,检索到此链表的头后赋值给headNode,并打印该结点的值;判断该结点是否有左右结点,依次加入链表的尾部;
实现代码:
//层序遍历--这里访问仍是打印该结点的值
public static void layerTranverse(BiTree bt){ if(bt.root==null){ System.out.println("null"); }else{ Queue<BiNode> q=new LinkedList<BiNode>(); q.add(bt.root); while(!q.isEmpty()){ //poll()-->检索并删除此链表的头(第一个元素)。 BiNode headNode=q.poll(); System.out.print(headNode.data); if(headNode.lchild!=null){ q.add(headNode.lchild); } if(headNode.rchild!=null){ q.add(headNode.rchild); } } } }
3.推导遍历结果
已知前序遍历和中序遍历序列,可以唯一确定一棵二叉树;已知后序遍历和中序遍历序列,可以唯一确定一棵二叉树序列。
3.1 由前序遍历和中序遍历序列,推导后序遍历结果
实现思路:
- 由前序遍历序列确定树的根结点,根结点为前序遍历序列的第一个结点;
- 由1得到的根结点及中序遍历序列确定根结点的左子树和右子树的结点有哪些,确立的这些结点序列分别为根结点左子树和右子树的中序遍历结果;
- 找到前序遍历序列中对应的根结点的左子树和右子树的前序遍历结果,至此确定了根结点的左子树和右子树的前序遍历序列以及中序遍历序列;
- 递归调用,构建根结点的左子树和右子树;
实现代码:
//根据前序遍历和中序遍历确定二叉树的后序遍历 //一.首先根据前序遍历和中序遍历确定二叉树 public static BiNode initTree(int[] preOrder,int start1,int end1,int[] mediumOrder,int start2,int end2){ if (start1>end1 || start2>end2){ return null; } //1.确立根结点-->根结点为前序遍历的第一个结点 int rootData=preOrder[start1]; BiNode headNode=new BiNode(rootData); //2.根据中序遍历结果确立根结点的左子树和右子树-->左子树和右子树结点有哪些 //中序遍历结果根结点左右的为左子树和右子树结点-->先要确立根结点在中序遍历结果中的索引位置 //方法不要写死,后面要递归调用 int rootIndex=findIndexInArray(mediumOrder,rootData,start2,end2); //3.确立根结点左子树的结点个数 int offset=rootIndex-start2-1; //4.构建头结点的左右子树 BiNode ltree=initTree(preOrder,start1+1,start1+offset+1,mediumOrder,start2,start2+offset); BiNode rtree=initTree(preOrder,start1+offset+2,end1,mediumOrder,rootIndex+1,end2); headNode.lchild=ltree; headNode.rchild=rtree; return headNode; }
3.2 由后序遍历和中序遍历序列,推导前序遍历结果
实现思路:
- 由后序遍历序列确定树的根结点,根结点为后序遍历的最后一个结点;
- 由1得到的根结点及中序遍历序列确定根结点的左子树和右子树的结点有哪些,确立的这些结点序列分别为根结点左子树和右子树的中序遍历结果;
- 找到后序遍历序列中对应的根结点的左子树和右子树的后序遍历结果,至此确定了根结点的左子树和右子树的后序遍历序列以及中序遍历序列;
- 递归调用,构建根结点的左子树和右子树;
实现代码:
public static BiNode initTree2(int[] postOrder,int start1,int end1,int[] mediumOrder,int start2,int end2){ if(start1>end1 || start2>end2){ return null; } int rootData=postOrder[end1]; BiNode headNode=new BiNode(rootData); int rootIndex=findIndexInArray(mediumOrder,rootData,start2,end2); int offset=end2-rootIndex; BiNode ltree=initTree2(postOrder,start1,end1-offset-1,mediumOrder,start2,rootIndex-1); BiNode rtree=initTree2(postOrder,end1-offset,end1-1,mediumOrder,rootIndex+1,end2); headNode.lchild=ltree; headNode.rchild=rtree; return headNode; }
4.完整代码
package com.java.Search; import java.util.LinkedList; import java.util.Queue; /* * 二叉树的前序,中序,后序遍历,层序遍历 * 以二叉排序树的特点创建一棵树 * */ public class SearchTest { public static void main(String[] args){ BiTree bt=new BiTree(); BiTree.insert(bt, 2); BiTree.insert(bt, 8); BiTree.insert(bt, 7); BiTree.insert(bt, 4); BiTree.insert(bt, 9); BiTree.insert(bt, 3); BiTree.insert(bt, 1); BiTree.insert(bt, 6); BiTree.insert(bt, 5); //System.out.println(bt); System.out.print("前序遍历:"); preorderTreverse(bt.root); System.out.print("中序遍历:"); mediumTreverse(bt.root); System.out.print("后序遍历:"); postorderTreverse(bt.root); //layerTranverse(bt); int[] preOrder={2,1,8,7,4,3,6,5,9}; int[] mediumOrder={1,2,3,4,5,6,7,8,9}; BiNode tree=initTree(preOrder,0,preOrder.length-1,mediumOrder,0,mediumOrder.length-1); System.out.println(""); System.out.println("由前序遍历和中序遍历得到的树为:"+tree); System.out.print("后序遍历的树为:"); postorderTreverse(tree); int[] postOrder={1,3,5,6,4,7,9,8,2}; BiNode tree2=initTree2(postOrder,0,postOrder.length-1,mediumOrder,0,mediumOrder.length-1); System.out.println(""); System.out.println("由后序遍历和中序遍历得到的树为:"+tree2); System.out.print("前序遍历的树为:"); preorderTreverse(tree2); } //前序遍历(根左右) public static void preorderTreverse(BiNode bt){ if(bt==null){ System.out.print(""); }else{ //访问根结点(访问定义为打印结点数值) System.out.print(bt.data); //前序遍历访问根结点的左子树 preorderTreverse(bt.lchild); //前序遍历访问根结点的右子树 preorderTreverse(bt.rchild); } } //中序遍历(左根右) public static void mediumTreverse(BiNode bt){ if(bt==null){ System.out.print(""); }else{ //中序遍历访问根结点的左子树 mediumTreverse(bt.lchild); //访问根结点 System.out.print(bt.data); //中序遍历访问根结点的右子树 mediumTreverse(bt.rchild); } } //后序遍历(左右根) public static void postorderTreverse(BiNode bt){ if(bt==null){ System.out.print(""); }else{ //后序遍历根结点的左子树 postorderTreverse(bt.lchild); //后序遍历根结点的右子树 postorderTreverse(bt.rchild); //访问根结点 System.out.print(bt.data); } } //层序遍历--这里访问仍是打印该结点的值 public static void layerTranverse(BiTree bt){ if(bt.root==null){ System.out.println("null"); }else{ Queue<BiNode> q=new LinkedList<BiNode>(); q.add(bt.root); while(!q.isEmpty()){ //poll()-->检索并删除此链表的头(第一个元素)。 BiNode headNode=q.poll(); System.out.print(headNode.data); if(headNode.lchild!=null){ q.add(headNode.lchild); } if(headNode.rchild!=null){ q.add(headNode.rchild); } } } } //由后序遍历和中序遍历确定二叉树的前序遍历 public static BiNode initTree2(int[] postOrder,int start1,int end1,int[] mediumOrder,int start2,int end2){ if(start1>end1 || start2>end2){ return null; } int rootData=postOrder[end1]; BiNode headNode=new BiNode(rootData); int rootIndex=findIndexInArray(mediumOrder,rootData,start2,end2); int offset=end2-rootIndex; BiNode ltree=initTree2(postOrder,start1,end1-offset-1,mediumOrder,start2,rootIndex-1); BiNode rtree=initTree2(postOrder,end1-offset,end1-1,mediumOrder,rootIndex+1,end2); headNode.lchild=ltree; headNode.rchild=rtree; return headNode; } //根据前序遍历和中序遍历确定二叉树的后序遍历 //一.首先根据前序遍历和中序遍历确定二叉树 public static BiNode initTree(int[] preOrder,int start1,int end1,int[] mediumOrder,int start2,int end2){ if (start1>end1 || start2>end2){ return null; } //1.确立根结点-->根结点为前序遍历的第一个结点 int rootData=preOrder[start1]; BiNode headNode=new BiNode(rootData); //2.根据中序遍历结果确立根结点的左子树和右子树-->左子树和右子树结点有哪些 //中序遍历结果根结点左右的为左子树和右子树结点-->先要确立根结点在中序遍历结果中的索引位置 //方法不要写死,后面要递归调用 int rootIndex=findIndexInArray(mediumOrder,rootData,start2,end2); //3.确立根结点左子树的结点个数 int offset=rootIndex-start2-1; //4.构建头结点的左右子树 BiNode ltree=initTree(preOrder,start1+1,start1+offset+1,mediumOrder,start2,start2+offset); BiNode rtree=initTree(preOrder,start1+offset+2,end1,mediumOrder,rootIndex+1,end2); headNode.lchild=ltree; headNode.rchild=rtree; return headNode; } //确立根结点在中序遍历结果中的索引位置 public static int findIndexInArray(int[] a,int x,int begin,int end){ int index=0; for(int i=begin;i<=end;i++){ if(a[i]==x){ index=i; } } return index; } }
运行结果:
前序遍历:218743659中序遍历:123456789后序遍历:135647982 由前序遍历和中序遍历得到的树为:BiNode [data=2, lchild=BiNode [data=1, lchild=null, rchild=null], rchild=BiNode [data=8, lchild=BiNode [data=7, lchild=BiNode [data=4, lchild=BiNode [data=3, lchild=null, rchild=null], rchild=BiNode [data=6, lchild=BiNode [data=5, lchild=null, rchild=null], rchild=null]], rchild=null], rchild=BiNode [data=9, lchild=null, rchild=null]]] 后序遍历的树为:135647982 由后序遍历和中序遍历得到的树为:BiNode [data=2, lchild=BiNode [data=1, lchild=null, rchild=null], rchild=BiNode [data=8, lchild=BiNode [data=7, lchild=BiNode [data=4, lchild=BiNode [data=3, lchild=null, rchild=null], rchild=BiNode [data=6, lchild=BiNode [data=5, lchild=null, rchild=null], rchild=null]], rchild=null], rchild=BiNode [data=9, lchild=null, rchild=null]]] 前序遍历的树为:218743659