二叉树的遍历与重建 Gentleman

1、二叉树的遍历

     是指从二叉树的根结点出发,按照某种次序依次访问二叉树中的所有结点,使得每个结点被访问一次,且仅被访问一次。
二叉树的访问次序可以分为四种:

前序遍历
中序遍历
后序遍历
层序遍历

1.1、前序遍历通俗的说就是从二叉树的根结点出发,当第一次到达结点时就输出结点数据,按照先向左在向右的方向访问。

 上图所示二叉树访问如下:

  
从根结点出发,则第一次到达结点A,故输出A;
继续向左访问,第一次访问结点B,故输出B;
按照同样规则,输出D,输出H;
当到达叶子结点H,返回到D,此时已经是第二次到达D,故不在输出D,进而向D右子树访问,D右子树不为空,则访问至I,第一次到达I,则输出I;
I为叶子结点,则返回到D,D左右子树已经访问完毕,则返回到B,进而到B右子树,第一次到达E,故输出E;
向E左子树,故输出J;
按照同样的访问规则,继续输出C、F、G;

作者:MrHorse1992
链接:https://www.jianshu.com/p/bf73c8d50dc2
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

则上图所示二叉树的前序遍历输出为: ABDHIEJCFG
      

1.2、中序遍历就是从二叉树的根结点出发,当第二次到达结点时就输出结点数据,按照先向左在向右的方向访问。

上图所示二叉树中序访问如下:

从根结点出发,则第一次到达结点A,不输出A,继续向左访问,第一次访问结点B,不输出B;继续到达D,H;
到达H,H左子树为空,则返回到H,此时第二次访问H,故输出H;
H右子树为空,则返回至D,此时第二次到达D,故输出D;
由D返回至B,第二次到达B,故输出B;
按照同样规则继续访问,输出J、E、A、F、C、G;

则上图所示二叉树的中序遍历输出为:HDIBJEAFCG

1.3、后序遍历就是从二叉树的根结点出发,当第三次到达结点时就输出结点数据,按照先向左在向右的方向访问。

上图所示二叉树后序访问如下:

从根结点出发,则第一次到达结点A,不输出A,继续向左访问,第一次访问结点B,不输出B;继续到达D,H;
到达H,H左子树为空,则返回到H,此时第二次访问H,不输出H;
H右子树为空,则返回至H,此时第三次到达H,故输出H;
由H返回至D,第二次到达D,不输出D;
继续访问至I,I左右子树均为空,故第三次访问I时,输出I;
返回至D,此时第三次到达D,故输出D;
按照同样规则继续访问,输出J、E、B、F、G、C,A;

则上图所示二叉树的后序遍历输出为:HIDJEBFGCA

当然前中后三种遍历方式,也可以简单的理解为:

前序就是 父节点->左子树->右子树,

中序是 左子树->父节点->右子树,

后序是 左子树 -> 右子树 ->父节点

递归在其中非常重要,每棵子树又可以重新遍历,依此类推,遍历完成。

1.4、层次遍历

层次遍历就是按照树的层次自上而下的遍历二叉树。针对上图所示二叉树的层次遍历结果为:ABCDEFGHIJ

2、二叉树重建

二叉树的重建,只能在提供前序+中序,或者后序+中序的情况下,才可以正常的重构。

    • 前序+中序:
      1、前序遍历数组中的第一个数就是根节点,得到根节点的数字。
      2、然后在中序遍历中找到该根节点的位置,中序数组的左边就是该根节点的左子树,中序遍历的右边序列是其右子树。
      3、前序遍历中根据中序遍历中根节点的位置,将前序遍历分为前序遍历的左子树和右子树。
      4、根节点就确定了,最后将前序和中序划分出来的左右子树,分别带入递归得到左右子树的构建情况。
    • 后序+中序
      1.后续遍历的最后一个节点是根节点,然后中序遍历中找出根节点位置mid。
      2.再在根据中序遍历中的mid位置将后序遍历数组和中序遍历数据分为左子树和右子树。
      3.最后将划分完成的左右子树递归。

Java代码实现如下:

public class RebuildBinaryTree {
    //假设输入的前序遍历和中序遍历的结果中都不含重复的数字
    /*
      前序遍历第一位是根节点;
      中序遍历中,根节点左边的是根节点的左子树,右边是根节点的右子树。
      例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}。
      首先,根节点 是{ 1 };
      左子树是:前序{ 2,4,7 } ,中序{ 4,7,2 };
      右子树是:前序{ 3,5,6,8 } ,中序{ 5,3,8,6 };
      这时,如果我们把左子树和右子树分别作为新的二叉树,则可以求出其根节点,左子树和右子树。
     */
/*
pre 前序序列数组
in 中序序列数组
*/
public TreeNode reConstructBinaryTree(int [] pre,int [] in) { if(pre==null||in==null){ return null; } TreeNode treeNode=reConstructBinaryTree(pre, in,0,pre.length-1,0,in.length-1); return treeNode; }

     /*
pre 前序序列数组
in 中序序列数组
preStart 前序序列第一个节点的索引值
preEnd 前序序列最后一个节点的索引值
inStart 中序序列第一个节点的索引值
inEnd 中序序列最后一个节点的索引值
     */
public TreeNode reConstructBinaryTree(int [] pre,int [] in,int preStart,int preEnd,int inStart,int inEnd) {
         TreeNode tree=new TreeNode(pre[preStart]);  //前序遍历的第一个是根节点,递归时把左子树和右子树分别作为新的二叉树
         tree.left=null;
         tree.right=null;
         if(preStart==preEnd&&inStart==inEnd){   //说明已经是树叶节点
             return tree;
         }
         int root;
         for(root=inStart;root<inEnd;root++){       //寻找根节点在中序遍历中的下标
             if(pre[preStart]==in[root]){
                 break;
             }
         }
         int leftLength=root-inStart;       //左子树的长度
         int rightLength=inEnd-root;        //右子树的长度
         //把左子树和右子树分别作为新的二叉树,则可以求出其根节点,左子树和右子树。
         if(leftLength>0){
             tree.left=reConstructBinaryTree(pre,in,preStart+1,preStart+leftLength,inStart,root-1);
         }
         if(rightLength>0){
             tree.right=reConstructBinaryTree(pre,in,preStart+1+leftLength,preEnd,root+1,inEnd);
         }
         return  tree;
    }
}
不忘初心,相信自己,坚持下去,付诸实施。
原文地址:https://www.cnblogs.com/controller666/p/14445314.html