Java树(进阶理解)

入门

TreeNode是经常用到的一个结构体,表示数据结构树(Tree)中的一个节点。其官方定义如下:

public class TreeNode {
	int val;
	TreeNode left;
	TreeNode right;
	TreeNode(int x) { val = x; }
}

在Tree的题目中,常会给出一些测试用例,用一些特定的格式来表示一棵树,如[3,9,20,null,null,15,7]就表示如下的一棵树:
在这里插入图片描述

特点

确定二叉树

问题1. 同时给定一棵二叉树的先序序列和中序序列,就能唯一确定这棵二叉树? 答案:是
问题2. 同时给定一棵二叉树的中序序列和后序序列,就能唯一确定这棵二叉树? 答案:是
问题3. 同时给定一棵二叉树的先序序列和后序序列,就能唯一确定这棵二叉树? 答案:不是

树的基本结构

package tree;  
  
public class TreeNode<T> {  
    T value;  
          
    TreeNode<T> leftChild;  
    TreeNode<T> rightChild;  
  
    TreeNode(T value) {  
        this.value = value;  
    }  
    TreeNode() {  
    }  
      
    /**   增加左子节点 
    * addLeft:  
    * @param value  
    * void  返回类型    
    */  
    public void addLeft(T value){  
        TreeNode<T> leftChild = new TreeNode<T>(value);  
        this.leftChild = leftChild;  
    }  
    /**   
    * addRight: 增加右子节点 
    * @param value  
    * void  返回类型    
    */  
    public void addRight(T value){  
        TreeNode<T> rightChild = new TreeNode<T>(value);  
        this.rightChild = rightChild;  
    }  
    /* (non-Javadoc) 
     * @see java.lang.Object#equals(java.lang.Object) 
     * 重载equal方法 
     */  
    @Override  
    public boolean equals(Object obj) {  
        // TODO Auto-generated method stub  
        if(!(obj instanceof TreeNode)){  
            return false;  
        }  
        return this.value.equals(((TreeNode<?>)obj).value);  
    }  
    /* (non-Javadoc) 
     * @see java.lang.Object#hashCode() 
     * 重载hashCode方法 
     */  
    @Override  
    public int hashCode() {  
        // TODO Auto-generated method stub  
        return this.value.hashCode();  
    }  
    @Override  
    public String toString(){  
        return this.value==null?"":this.value.toString();  
    }  
      
      
}

树的操作类

package tree;  
  
import java.util.ArrayList;  
import java.util.LinkedList;  
import java.util.List;  
import java.util.Queue;  
  
/** 
 * TreeTools:树的操作类 
 *  
 * @author xuejupo jpxue@travelsky.com 
 *  
 *         create in 2015-11-19 下午5:31:05 
 *  
 */  
public class TreeTools {  
  
    /** 
     * getTreeNum: 判断树中节点个数 
     *  
     * @param root 
     *            根节点 
     * @return int 返回类型 
     */  
    public static <T> int getTreeNum(TreeNode<T> root) {  
        if (root == null) {  
            return 0;  
        }  
        return getTreeNum(root.leftChild) + getTreeNum(root.rightChild) + 1;  
    }  
  
    /** 
     * getTreeDepth: 判断树的深度 
     *  
     * @param root 
     *            根节点 
     * @return int 返回类型 
     */  
    public static <T> int getTreeDepth(TreeNode<T> root) {  
        if (root == null) {  
            return 0;  
        }  
        int leftDepth = getTreeDepth(root.leftChild) + 1;  
        int rightDepth = getTreeDepth(root.rightChild) + 1;  
        return Math.max(leftDepth, rightDepth);  
    }  
  
    /** 
     * preOrderTravel: 前序遍历 
     *  
     * @param root 
     *            void 返回类型 
     */  
    public static <T> void preOrderTravel(TreeNode<T> root) {  
        if (root == null) {  
            return;  
        }  
        visitNode(root);  
        preOrderTravel(root.leftChild);  
        preOrderTravel(root.rightChild);  
    }  
  
    /** 
     * midOrderTravel: 中序遍历 
     *  
     * @param root 
     *            void 返回类型 
     */  
    public static <T> void midOrderTravel(TreeNode<T> root) {  
        if (root == null) {  
            return;  
        }  
        midOrderTravel(root.leftChild);  
        visitNode(root);  
        midOrderTravel(root.rightChild);  
    }  
  
    /** 
     * backOrderTravel: 后序遍历 
     *  
     * @param root 
     *            void 返回类型 
     */  
    public static <T> void backOrderTravel(TreeNode<T> root) {  
        if (root == null) {  
            return;  
        }  
        backOrderTravel(root.leftChild);  
        backOrderTravel(root.rightChild);  
        visitNode(root);  
    }  
  
    /** 
     * visitNode: 访问node节点 
     *  
     * @param node 
     *            void 返回类型 
     */  
    private static <T> void visitNode(TreeNode<T> node) {  
        System.out.print(node.value + "\t");  
    }  
  
    /** 
     * levelTravel: 分层遍历 
     *  
     * @param root 
     *            void 返回类型 
     */  
    public static <T> void levelTravel(TreeNode<T> root) {  
        Queue<TreeNode<T>> q = new LinkedList<TreeNode<T>>();  
        q.offer(root);  
        while (!q.isEmpty()) {  
            TreeNode<T> temp = q.poll();  
            visitNode(temp);  
            if (temp.leftChild != null) {  
                q.offer(temp.leftChild);  
            }  
            if (temp.rightChild != null) {  
                q.offer(temp.rightChild);  
            }  
        }  
    }  
  
    /** 
     * getNumForKlevel: 求第K层节点个数 
     *  
     * @param root 
     * @param k 
     * @return int 返回类型 
     */  
    public static <T> int getNumForKlevel(TreeNode<T> root, int k) {  
        if (root == null || k < 1) {  
            return 0;  
        }  
        if (k == 1) {  
            return 1;  
        }  
        int leftNum = getNumForKlevel(root.leftChild, k - 1);  
        int rightNum = getNumForKlevel(root.rightChild, k - 1);  
        return leftNum + rightNum;  
    }  
  
    /** 
     * getLeafNum: 求二叉树中叶子节点的个数 
     *  
     * @param root 
     * @return int 返回类型 
     */  
    public static <T> int getLeafNum(TreeNode<T> root) {  
        if (root == null) {  
            return 0;  
        }  
        if (root.leftChild == null && root.rightChild == null) {  
            return 1;  
        }  
        int leftNum = getLeafNum(root.leftChild);  
        int rightNum = getLeafNum(root.rightChild);  
        return leftNum + rightNum;  
    }  
  
    /** 
     * exchange: 交换根节点的左右子树 
     *  
     * @param root 
     * @return TreeNode 返回类型 
     */  
    public static <T> TreeNode<T> exchange(TreeNode<T> root) {  
        if (root == null) {  
            return null;  
        }  
        TreeNode<T> left = exchange(root.leftChild);  
        TreeNode<T> right = exchange(root.rightChild);  
        root.leftChild = right;  
        root.rightChild = left;  
        return root;  
    }  
  
    /** 
     * nodeIsChild: 查看node是否是root的子节点 
     *  
     * @param root 
     * @param node 
     * @return boolean 返回类型 
     */  
    public static <T> boolean nodeIsChild(TreeNode<T> root, TreeNode<T> node) {  
        if (root == null || node == null) {  
            return false;  
        }  
        if (root == node) {  
            return true;  
        }  
        boolean isFind = nodeIsChild(root.leftChild, node);  
        if (!isFind) {  
            isFind = nodeIsChild(root.rightChild, node);  
        }  
        return isFind;  
    }  
  
    /** 
     * findAllFatherNode: 返回两个节点lnode和rnode的以root为根节点的公共父节点 
     *  
     * @param root 
     *            根节点 
     * @param lNode 
     * @param rNode 
     * @return TreeNode 返回类型 
     */  
    public static <T> TreeNode<T> findAllFatherNode(TreeNode<T> root,  
            TreeNode<T> lNode, TreeNode<T> rNode) {  
        if (lNode == root || rNode == root) {  
            return root;  
        }  
        if (root == null || lNode == null || rNode == null) {  
            return null;  
        }  
        // 如果lNode是左子树的节点  
        if (nodeIsChild(root.leftChild, lNode)) {  
            if (nodeIsChild(root.rightChild, rNode)) {  
                return root;  
            } else {  
                return findAllFatherNode(root.leftChild, lNode, rNode);  
            }  
        } else {  
            if (nodeIsChild(root.leftChild, rNode)) {  
                return root;  
            } else {  
                return findAllFatherNode(root.rightChild, lNode, rNode);  
            }  
        }  
    }  
  
    /** 
     * getTreeFromPreAndMid: 根据前序和中序构建二叉树 
     *  
     * @param pre 
     *            前序序列 
     * @param mid 
     *            中序序列 
     * @return TreeNode 返回类型 
     */  
    public static <T> TreeNode<T> getTreeFromPreAndMid(List<T> pre, List<T> mid) {  
        if (pre == null || mid == null || pre.size() == 0 || mid.size() == 0) {  
            return null;  
        }  
        if (pre.size() == 1) {  
            return new TreeNode<T>(pre.get(0));  
        }  
        TreeNode<T> root = new TreeNode<T>(pre.get(0));  
        // 找出根节点在中序中的位置  
        int index = 0;  
        while (!mid.get(index++).equals(pre.get(0))) {  
        }  
        // 构建左子树的前序  
        List<T> preLeft = new ArrayList<T>(index);  
        // 左子树的中序  
        List<T> midLeft = new ArrayList<T>(index);  
        for (int i = 1; i < index; i++) {  
            preLeft.add(pre.get(i));  
        }  
        for (int i = 0; i < index - 1; i++) {  
            midLeft.add(mid.get(i));  
        }  
  
        // 重建左子树  
        root.leftChild = getTreeFromPreAndMid(preLeft, midLeft);  
        // 右子树的前序  
        List<T> preRight = new ArrayList<T>(pre.size() - index - 1);  
        // 右子树的中序  
        List<T> midRight = new ArrayList<T>(pre.size() - index - 1);  
        for (int i = 0; i <= pre.size() - index - 1; i++) {  
            preRight.add(pre.get(index + i));  
        }  
        for (int i = 0; i <= pre.size() - index - 1; i++) {  
            midRight.add(mid.get(index + i));  
        }  
        // 重建→子树  
        root.rightChild = getTreeFromPreAndMid(preRight, midRight);  
        return root;  
    }  
  
    /** 
     * equals: 查看node1和node2两棵树是否相等(两棵树所有节点都相等) 
     *  
     * @param node1 
     *            node2 两个节点 
     * @return boolean 返回类型 
     */  
    public static <T> boolean equals(TreeNode<T> node1, TreeNode<T> node2) {  
        // TODO Auto-generated method stub  
        if (node1 == null && node2 == null) {  
            return true;  
        } else if (node1 == null || node2 == null) {  
            return false;  
        }  
        boolean isEqual = node1.value.equals(node2.value);  
        boolean isLeftEqual = equals(node1.leftChild, node2.leftChild);  
        boolean isRightEqual = equals(node1.rightChild, node2.rightChild);  
        return isEqual && isLeftEqual && isRightEqual;  
    }  
}

测试类

public static void main(String[] args) {
        // TODO Auto-generated method stub
        TreeNode<Integer> t = new TreeNode<Integer>(1);
        t.addLeft(2);
        t.addRight(3);
        t.leftChild.addLeft(4);
        t.leftChild.addRight(5);
        System.out.println("中序遍历测试:");
        TreeTools.midOrderTravel(t);
        System.out.println("\n前序遍历测试:");
        TreeTools.preOrderTravel(t);
        System.out.println("\n后序遍历测试:");
        TreeTools.backOrderTravel(t);
        System.out.println("\n层次遍历测试:");
        TreeTools.levelTravel(t);
        System.out.println("\n树的深度:"+TreeTools.getTreeDepth(t));
        System.out.println("树的叶子个数:"+TreeTools.getLeafNum(t));
        System.out.println("树的节点个数:"+TreeTools.getTreeNum(t));
        System.out.println("第2层节点个数为:"+TreeTools.getNumForKlevel(t,2));
        List<Integer> pre = new ArrayList<Integer>();
        pre.add(1);
        pre.add(2);
        pre.add(4);
        pre.add(5);
        pre.add(3);
        List<Integer> mid = new ArrayList<Integer>();
        mid.add(4);
        mid.add(2);
        mid.add(5);
        mid.add(1);
        mid.add(3);
        TreeNode<Integer> root = TreeTools.getTreeFromPreAndMid(pre, mid);
        System.out.println("\n通过前序和中序构建树测试:");
        TreeTools.levelTravel(root);
        System.out.println("\n构建的树比较测试:");
        System.out.println(TreeTools.equals(t,root));
    }

结果:
-----------------------------------------------
中序遍历测试:
4 2 5 1 3
前序遍历测试:
1 2 4 5 3
后序遍历测试:
4 5 2 3 1
层次遍历测试:
1 2 3 4 5
树的深度:3
树的叶子个数:3
树的节点个数:5
第2层节点个数为:2

通过前序和中序构建树测试:
1 2 3 4 5
构建的树比较测试:
true
-----------------------------------------------

经典例题

以层次结构输入一颗树

输入为整数n,层次遍历向二叉树中填入1、2、3…n-1、n,形成一颗二叉树。
参考链接:https://blog.csdn.net/feng11116/article/details/50481713?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase

import java.util.LinkedList;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int lastCount = 0;
        while(scanner.hasNext()){
            int n = scanner.nextInt();
            if (n == 0){
                break;
            }
            Tree t =  MakeTree(1, n);//树构造成功
        }
    }
    class Tree{//树节点结构
        private int val;
        private Tree leftleaf;
        private Tree rightleaf;
        public Tree(int val){
            this.val = val;
            this.leftleaf = null;
            this.rightleaf = null;
        }
        public int getVal(){
            return this.val;
        }
        public Tree getLeftleaf(){
            return this.leftleaf;
        }
        public Tree getRightleaf(){
            return this.rightleaf;
        }
        public void setVal(int val) {
            this.val = val;
        }
        public void setLeftleaf(Tree leftleaf) {
            this.leftleaf = leftleaf;
        }
        public void setRightleaf(Tree rightleaf) {
            this.rightleaf = rightleaf;
        }
    }

    层次遍历造树
    public static Tree MakeTree(int i,int n){
        if (n == 0)
            return null;
        else if (i <= n){
            Tree root = new Main().new Tree(i);
            System.out.println(root.getVal());
            root.setLeftleaf(MakeTree(i*2,n));
            root.setLeftleaf(MakeTree(i*2+1,n));
            return root;
        }else{
            return null;
        }
    }
}



FBI树

我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。
FBI树是一种二叉树,它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2N的“01”串S可以构造出一棵FBI树T,递归的构造方法如下:
1)T的根结点为R,其类型与串S的类型相同;
2)若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1和S2;由左子串S1构造R的左子树T1,由右子串S2构造R的右子树T2。
现在给定一个长度为2N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历序列。

数据规模和约定
对于全部的数据,N < = 10。
注:
[1] 二叉树:二叉树是结点的有限集合,这个集合或为空集,或由一个根结点和两棵不相交的二叉树组成。这两棵不相交的二叉树分别称为这个根结点的左子树和右子树。
[2] 后序遍历:后序遍历是深度优先遍历二叉树的一种方法,它的递归定义是:先后序遍历左子树,再后序遍历右子树,最后访问根。

输入
第一行是一个整数N(0 < = N < = 10),第二行是一个长度为2N的“01”串。
输出
包括一行,这一行只包含一个字符串,即FBI树的后序遍历序列。
样例输入
3
10001011
样例输出
IBFBBBFIBFIIIFF

在这里插入图片描述

import java.util.LinkedList;
import java.util.Scanner;

public class Main {
    static String str = "";
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int n1 = 1 << n;//左移等于1*2的n次方,右移等于-/(2的n次方)
        //System.out.println(n);
        String s = scanner.next();
        //System.out.println(s);
        Main.FBITree t = new Main().new FBITree(s,n1);//调用内部类的构造函数,通过外部类对象调用

        houxu(t.root);
        System.out.println(str);
    }
    class Tree{//树结构
        private String val;
        private Tree leftleaf;
        private Tree rightleaf;
        public Tree(String val){
            this.val = val;
            this.leftleaf = null;
            this.rightleaf = null;
        }
        public String getVal(){
            return this.val;
        }
        public Tree getLeftleaf(){
            return this.leftleaf;
        }
        public Tree getRightleaf(){
            return this.rightleaf;
        }
        public void setVal(String val) {
            this.val = val;
        }
        public void setLeftleaf(Tree leftleaf) {
            this.leftleaf = leftleaf;
        }
        public void setRightleaf(Tree rightleaf) {
            this.rightleaf = rightleaf;
        }
    }
    public static void houxu(Tree t){//树的后续遍历
        if (t != null){
            houxu(t.leftleaf);
            houxu(t.rightleaf);
            str = str + t.getVal();
        }
    }

    //注意:这是一个内部类在调用该类时需通过外部类对象来调用内部类
    class FBITree{//根据题目要求建树过程
        Tree root;
        public FBITree(String str,int count){
            if(count>0) {
                if(str.indexOf("0")!=-1&&str.indexOf("1")!=-1)//str中间即存在0又存在1
                    root=new Tree("F");
                else if(str.indexOf("0")!=-1&&str.indexOf("1")==-1)//str中只存在0
                    root=new Tree("B");
                else
                    root=new Tree("I");//str中只存在1
                String lStr=str.substring(0,str.length()/2);
                String rStr=str.substring(str.length()/2,str.length());
                root.setLeftleaf(new FBITree(lStr,str.length()/2).root);
                root.setRightleaf(new FBITree(rStr,str.length()/2).root);
            }
        }
    }
}



感谢:
链接: https://www.cnblogs.com/dawnyxl/p/9047437.html.
链接: https://blog.csdn.net/skylibiao/article/details/81195219.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值