Thread Tree(线索二叉树)

Thread Tree(线索二叉树)

  1. 定义:在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如先序、中序、后序或层次等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化。
  2. 二叉树的遍历本质上是将一个复杂的非线性结构转换为线性结构,使每个结点都有了唯一前驱和后继(第一个结点无前驱,最后一个结点无后继)。对于二叉树的一个结点,查找其左右子女是方便的,其前驱后继只有在遍历中得到。为了容易找到前驱和后继,有两种方法。一是在结点结构中增加向前和向后的指针,这种方法增加了存储开销,不可取;二是利用二叉树的空链(左右孩子的)指针。
  3. 结构:线索二叉树中的线索能记录每个结点前驱和后继信息。为了区别线索指针和孩子指针,在每个结点中设置两个标志ltag和rtag。
    当tag和rtag为0时,leftChild和rightChild分别是指向左孩子和右孩子的指针;否则,leftChild是指向结点前驱的线索(pre),rightChild是指向结点的后继线索(suc)。由于标志只占用一个二进位,每个结点所需要的存储空间节省很多。
    线索二叉树链域
    其中:ltag=0 时lchild指向左儿子;ltag=1 时lchild指向前驱;rtag=0 时rchild指向右儿子;rtag=1 时rchild指向后继。
  4. java代码实现:
    Node节点类:
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;
    }
    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;
    }
}

中序线索二叉树

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(){
        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){
        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);
    }
}

你可能感兴趣的:(数据结构与算法,二叉树,java,数据结构,算法)