中序线索二叉树(ThreadBinaryTree)——基本概念及Java实现

关于二叉树的基本概念,见blog:二叉树基本概念

1. 为什么要使用线索二叉树

  普通二叉树有很多浪费的空间,比如叶节点的左右孩子的引用都是空的。我们可以利用起这些浪费的空间,来帮助我们实现中序遍历。我们使引用按顺序遍历后继节点,这个引用就称为线索(thread)。

2. 线索二叉树的数据结构

  为了区分左右孩子和前后线索,我们需要为左右孩子加一个布尔值作为标记。

leftChild

isPred

data

isSucc

rightChild

3. 线索二叉树的分类

  根据根节点遍历的顺序,可以得到三种线索二叉树:先序线索二叉树、中序线索二叉树、后序线索二叉树。

                                                               
                                                                                            二叉树
                                           
                                                                                     先序线索二叉树
                                          
                                                                                     中序线索二叉树
                                     
                                                                                     后序线索二叉树

4. 中序线索二叉树Java实现

实现方法:

 *     isEmpty():判断线索二叉树是否为空。
 *     clear():清空线索二叉树。
 *     add(TBTNode,Type):向指定节点添加包含指定数据的孩子。
 *     threadingTreeByLDR():将二叉树中序线索化。
 *     height():获取线索二叉树的高度。

首先来线索二叉树的节点(TBTNode)代码:

public class TBTNode<Type> {
    /**
     * left:左孩子或前驱
     * right:右孩子或后继
     * isPre:left是否为前驱
     * isSuc:right是否为后继
     * data:节点自身数据
     */
    private TBTNode<Type> left;
    private TBTNode<Type> right;
    private boolean isPre;
    private boolean isSuc;
    private Type data;

    /**
     * 两种构造方法
     */
    public TBTNode(){}

    public TBTNode(Type data){
        this.data = data;
    }

    /**
     * 五种属性的 get() 方法
     */
    public TBTNode<Type> getLeft() {
        return left;
    }

    public TBTNode<Type> getRight() {
        return right;
    }

    public Type getData() {
        return data;
    }

    public boolean isPre() {
        return isPre;
    }

    public boolean isSuc() {
        return isSuc;
    }

    /**
     * 五种属性的 set() 方法
     */
    public void setLeft(TBTNode<Type> left) {
        this.left = left;
    }

    public void setRight(TBTNode<Type> right) {
        this.right = right;
    }

    public void setData(Type data) {
        this.data = data;
    }

    public void setPre(boolean pre) {
        isPre = pre;
    }

    public void setSuc(boolean suc) {
        isSuc = suc;
    }
}

下面是线索二叉树(ThreadBinaryTree)的实现代码 :

/**
 * 中序线索二叉树的实现过程,实现的方法包括:
 *     isEmpty():判断线索二叉树是否为空。
 *     clear():清空线索二叉树。
 *     add(TBTNode,Type):向指定节点添加包含指定数据的孩子。
 *     threadingTreeByLDR():将二叉树中序线索化。
 *     height():获取线索二叉树的高度。
 * 其他的方法后序再更吧。
 */
public class ThreadBinaryTree<Type> {

    private TBTNode<Type> root;
    private TBTNode<Type> previous;

    /**
     * 两种构造方法
     */
    public ThreadBinaryTree(){//空构造方法

    }

    public ThreadBinaryTree(TBTNode<Type> root){//给定根节点的构造方法
        this.root = root;
    }

    /**
     * root 的 get() 和 set() 方法
     */
    public TBTNode<Type> getRoot() {
        return root;
    }

    public void setRoot(TBTNode<Type> root) {
        this.root = root;
    }

    /**
     * 判断线索二叉树是否为空树
     */
    public boolean isEmpty(){
        return (root == null);
    }

    /**
     * 清空线索二叉树
     */
    public void clear(){
        root = null;
    }

    /**
     * 向指定节点添加
     * @param parent
     * @param data
     * @return
     */
    public boolean add(TBTNode<Type> parent, Type data){
        TBTNode temp;
        if (!parent.isPre() && !parent.isSuc() //left和right都是孩子,而且孩子都存在
                && (parent.getLeft() != null) && (parent.getRight() != null))
            return false;//添加节点失败
        TBTNode<Type> newNode = new TBTNode<>(data);
        if (parent.isPre()){//node的left是前驱
            temp = parent.getLeft();//保存前驱引用
            parent.setLeft(newNode);//设置node的left为新节点
            parent.setPre(false);//设置node的前驱为false
            newNode.setLeft(temp);//设置新节点的left为pre
            newNode.setPre(true);//设置新节点的前驱为true
        }else if (parent.getLeft() == null){//左节点为空
            parent.setLeft(newNode);//直接设置
            parent.setPre(false);
        }else if (parent.isSuc()){
            temp = parent.getRight();
            parent.setRight(newNode);
            parent.setSuc(false);
            newNode.setRight(temp);
            newNode.setSuc(true);
        }else {
            parent.setRight(newNode);
            parent.setSuc(false);
        }
        return true;
    }
    /**
     * 将二叉树中序线索化
     * @param :进行线索化的树
     */
    public void threadingTreeByLDR(){
//        TBTNode<Type> current = root;
//        while (current.getLeft() != null){
//            current = current.getLeft();
//        }
//        threadingTreeByLDR(current);
        threadingTreeByLDR(root);
    }

    /**
     * 将二叉树中序线索化的详细实现
     * 参与变量:
     *     current:被线索化的当前节点
     *     previous:被线索化的前一个节点
     * Q:为什么需要previous?
     * A:设置前驱线索和后继线索的时候,有两个节点参与,这两个节点就是previous和current。
     * 算法过程:
     *     1.对每个节点:如果current不为空,则递归线索化current的左子树; 否则返回。
     *     2.如果current没有左孩子,则current的左引用为前驱线索,故设置 current.setPre(true);
     *     3.如果current没有右孩子,则current的右引用为后继线索,故设置 current.setSuc(true);
     *     4.如果previous不为空,且previous的右引用为后继线索,则设置previous的右引用为current;
     *     5.如果current的左引用为前驱线索,则设置current的左引用为previous。
     * 算法完。
     * @param current 根节点
     */
    private void threadingTreeByLDR(TBTNode<Type> current){
        //previous 节点:前一个被线索化的节点
        if (current != null){
            threadingTreeByLDR(current.getLeft());//递归线索化左子树
            if (null == current.getLeft())//current 的左引用为空
                current.setPre(true);//设置 current 的左引用为前驱线索
            else
                current.setPre(false);//否则设置 current 的左引用为左孩子
            if(null == current.getRight())//current 的右引用为空
                current.setSuc(true);//设置 current 的右引用为后继线索
            else
                current.setSuc(false);//否则设置 current 的右引用为右孩子
            if (null != previous){//previous 不为空
                if (previous.isSuc()) //previous 的右引用是后继线索
                    previous.setRight(current);//设置 previous 的后继线索为 current
                if (current.isPre())//root 的左引用为前驱线索
                    current.setLeft(previous);//将 previous 设置为 current 的前驱线索
            }
            previous = current;//将 previous 设置为 current
            threadingTreeByLDR(current.getRight());//递归线索化右子树
        }
    }

    public int height(){
        return height(root);
    }

    /**
     * 计算线索二叉树的高度
     * @param current:当前线索二叉树的根节点
     * @return 线索二叉树的高度
     */
    private int height(TBTNode current){
        if (current == null)//中序线索化时,最左下角的节点左引用为null,最右下角的节点右引用为null,必须要注意
            return 0;//为空的地方不能算入
        if (current.isPre() || current.isSuc())//用于判断是不是叶子节点,叶子节点也必须算一个高度
            return 1;
        int h1 = height(current.getLeft());
        int h2 = height(current.getRight());
        return (h1 > h2) ? (h1 + 1):(h2 + 1);
    }
}

代码不够规范,还望多多包涵~ /bq

猜你喜欢

转载自blog.csdn.net/weixin_42034276/article/details/87932773