二叉树之线索链表

上一章介绍了二叉树的二叉链表的实现,并给出了相关遍历算法,但是,当以二叉链表作为二叉树的存储结构时,无法直接得到结点在任一序列中的前驱与后继信息。为了规避这个弊端,本章将引入线索二叉树的概念 ,并给出相关Java实现。

为了得到前驱与后继的信息,最直观的想法是在每个结点上添加两个指针域,分别指向结点在遍历时的前驱及后继。然而,这样会使存储密度大大降低,而在有n个结点的二叉树中有n+1个空链域,故可利用这些空链域来存放结点的前驱及后继信息,并作如下规定:若结点有左子树,则其leftChild域指向其左孩子,否则,令leftChild指向其前驱;若结点有右子树,则其rightChild域指向其右孩子,否则,令rightChild指向其后继。

为了避免混淆,现对上章中的结点结构作适当改动,改动后如下:

leftChiltleftTagelementrightTagrightChild

并规定:leftTag取值为false时,指示结点的左孩子,取值为true时,指示结点的前驱;rightTag取值为false时,指示结点的右孩子,取值为true时,指示结点的后继。

线索链表:以上述结点结构构成的二叉链表作为二叉树的存储结构,其中指示结点前驱及后继信息的指针称作线索,加上线索的二叉树称为线索二叉树。
线索化:对二叉树以某种次序遍历使其变为线索二叉树的过程。
线索化的实质:修改空指针(将二叉链表中的空指针改为指向前驱或后继的线索)。

下面给出相关java代码实现:

1. 二叉链表结点

package org.sky.tree;

/**
 *线索链表结点
 * 
 * @author sky
 *
 * @param <E>
 */
public class ThreadedBinaryTreeNode<E> {
    E element;
    //左右孩子指针(引用)
    ThreadedBinaryTreeNode<E> leftChild;
    ThreadedBinaryTreeNode<E> rightChild;
    //左右线索标识: true为线索,false为指针
    boolean leftTag;
    boolean rightTag;

    public ThreadedBinaryTreeNode() {
        super();
        this.element = null;
        this.leftChild = null;
        this.rightChild = null;
        this.leftTag = false;       //默认值一定要设为false
        this.rightTag = false;      //默认值一定要设为false
    }

    public ThreadedBinaryTreeNode(E element) {
        super();
        this.element = element;
        this.leftChild = null;
        this.rightChild = null;
        this.leftTag = false;       //默认值一定要设为false
        this.rightTag = false;      //默认值一定要设为false
    }

    public ThreadedBinaryTreeNode(E element,
            ThreadedBinaryTreeNode<E> leftChild,
            ThreadedBinaryTreeNode<E> rightChild, boolean leftTag,
            boolean rightTag) {
        super();
        this.element = element;
        this.leftChild = leftChild;
        this.rightChild = rightChild;
        this.leftTag = leftTag;
        this.rightTag = rightTag;
    }

    public E getElement() {
        return element;
    }

    public void setElement(E element) {
        this.element = element;
    }

    public ThreadedBinaryTreeNode<E> getLeftChild() {
        return leftChild;
    }

    public void setLeftChild(ThreadedBinaryTreeNode<E> leftChild) {
        this.leftChild = leftChild;
    }

    public ThreadedBinaryTreeNode<E> getRightChild() {
        return rightChild;
    }

    public void setRightChild(ThreadedBinaryTreeNode<E> rightChild) {
        this.rightChild = rightChild;
    }

    public boolean isLeftTag() {
        return leftTag;
    }

    public void setLeftTag(boolean leftTag) {
        this.leftTag = leftTag;
    }

    public boolean isRightTag() {
        return rightTag;
    }

    public void setRightTag(boolean rightTag) {
        this.rightTag = rightTag;
    }
}

2. 线索链表实现

package org.sky.tree;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * 二叉树的线索链表实现
 * 
 * @author sky
 *
 * @param <E>
 */
public class ThreadedBinaryTree<E> {

    private ThreadedBinaryTreeNode<E> root;         //二叉链表根结点
    private ThreadedBinaryTreeNode<E> threadedRoot; //线索链表头结点
    private ThreadedBinaryTreeNode<E> preVisit = null; //线索化时用,用于指向刚刚访问过的结点

    public ThreadedBinaryTree() {
        super();
        this.root = new ThreadedBinaryTreeNode<E>();
    }

    public boolean isEmpty() {
        if (root == null) {
            return true;
        }
        return false;
    }

    public ThreadedBinaryTreeNode<E> getThreadedRoot() {
        return threadedRoot;
    }

    public ThreadedBinaryTreeNode<E> getRoot() {
        return root;
    }   

    /**
     * 将传入的数据随机分配在二叉树的结点,以生成二叉链表
     * @param node
     * @param element
     */
    public void createTreeRandomly(ThreadedBinaryTreeNode<E> node, E element){
        if(root == null){
            root = new ThreadedBinaryTreeNode<E>(element);
        }else{
            if(Math.random() > 0.5){
                if(node.leftChild == null){
                    node.leftChild = new ThreadedBinaryTreeNode<E>(element);
                }else{
                    createTreeRandomly(node.leftChild,element);
                }
            }else{
                if(node.rightChild == null){
                    node.rightChild = new ThreadedBinaryTreeNode<E>(element);
                }else{
                    createTreeRandomly(node.rightChild,element);
                }
            }
        }
    }

    /**
     * 根据传入的集合创建完全二叉树
     * 此处利用了完全二叉树父结点和子结点间的关系:如果i=1,则结点i为根结点,否则其双亲结点为[i/2];
     *                              如果2i > n,则结点i无左孩子,否则其左孩子结点为2i;
     *                              如果2i+1 > n,则结点i无右孩子,否则右孩子结点为2i+1;
     * @param c
     */
    public void createCompleteBinaryTree(Collection<? extends E> c){
        if(c != null && c.size() > 0){
            List<ThreadedBinaryTreeNode<E>> treeList = new LinkedList<ThreadedBinaryTreeNode<E>>();
            for(Object o : c){              
                ThreadedBinaryTreeNode<E> threadedBinaryTreeNode = new ThreadedBinaryTreeNode<E>((E)o);
                treeList.add(threadedBinaryTreeNode);
            }

            LinkedBlockingDeque<ThreadedBinaryTreeNode<E>> queue = new LinkedBlockingDeque<ThreadedBinaryTreeNode<E>>();
            //对前treeList.size()/2 - 1个父节点按照父节点与孩子节点的数字关系建立二叉树 
            for(int parentIndex =  0; parentIndex < treeList.size()/2; parentIndex++){              
                if(parentIndex == 0){
                    root = treeList.get(parentIndex);
                    //左子树
                    root.leftChild = treeList.get(parentIndex*2 + 1);
                    queue.add(root.leftChild);
                    //右子树
                    root.rightChild = treeList.get(parentIndex*2 +2);
                    queue.add(root.rightChild);
                }else{
                    if(!queue.isEmpty() && parentIndex*2+1 < treeList.size()){
                        ThreadedBinaryTreeNode<E> node = (ThreadedBinaryTreeNode<E>) queue.poll();
                        if(parentIndex*2+1 < treeList.size()){
                            //左子树
                            node.leftChild = treeList.get(parentIndex*2 + 1);
                            queue.add(node.leftChild);
                        }
                        if(parentIndex*2+2 < treeList.size()){
                            //右子树
                            node.rightChild = treeList.get(parentIndex*2 + 2);
                            queue.add(node.rightChild);
                        }
                    }else{
                        return ;
                    };
                }
            }           
        }
    }

    /**
     * 中序遍历二叉树,并将其中序线索化 
     * 注:线索化的过程即为遍历的过程中修改空指针的过程
     */
    public void inOrderThreading(ThreadedBinaryTreeNode<E> root){
        if(threadedRoot == null){
            threadedRoot = new ThreadedBinaryTreeNode<E>();
        }
        threadedRoot.leftTag = false;
        threadedRoot.rightTag = true;
        threadedRoot.rightChild = threadedRoot;  //右指针回指
        if(root == null){
            threadedRoot.leftChild = threadedRoot; 
        }else{
            threadedRoot.leftChild = root;
            preVisit = threadedRoot;
            inThreading(root);      //中序线索化
            preVisit.rightTag = true;
            preVisit.rightChild = threadedRoot;
            threadedRoot.rightChild = preVisit;
        }       
    }
    //中序遍历进行中序线索化
    public void inThreading(ThreadedBinaryTreeNode<E> currentVisit){        
        if(currentVisit != null){
            inThreading(currentVisit.leftChild);    //左子树线索化
            if(currentVisit.leftChild == null){     //前驱线索
                currentVisit.leftTag = true;
                currentVisit.leftChild = preVisit;
            }
            if(preVisit.rightChild == null){        //后继线索
                preVisit.rightTag = true;
                preVisit.rightChild = currentVisit;
            }
            preVisit = currentVisit;                //保持preVisit指向刚刚访问过的结点
            inThreading(currentVisit.rightChild);   //右子树线索化
        }
    }

    /**
     * 中序遍历线索二叉树
     */
    public void inOrderTraverse(ThreadedBinaryTreeNode<E> threadedRoot){
        ThreadedBinaryTreeNode<E> pointer = threadedRoot.leftChild; //指向根结点
        while(pointer != threadedRoot){
            while(pointer.leftTag == false){
                pointer = pointer.leftChild;
            }
            visit(pointer);                  //访问左子树为空的结点
            while(pointer.rightTag == true && pointer.rightChild != threadedRoot){
                //访问后继结点
                pointer = pointer.rightChild;  
                visit(pointer);
            }
            pointer = pointer.rightChild;
        }
    }

    /**
     * 访问当前结点
     * @param current
     */
    public void visit(ThreadedBinaryTreeNode<E> current){
        if(current != null && current.element != null){
            System.out.println(current.element);
        }else{
            System.out.println("null");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值