1. 程式人生 > >二叉樹之線索連結串列

二叉樹之線索連結串列

上一章介紹了二叉樹的二叉連結串列的實現,並給出了相關遍歷演算法,但是,當以二叉連結串列作為二叉樹的儲存結構時,無法直接得到結點在任一序列中的前驅與後繼資訊。為了規避這個弊端,本章將引入線索二叉樹的概念 ,並給出相關Java實現。

為了得到前驅與後繼的資訊,最直觀的想法是在每個結點上新增兩個指標域,分別指向結點在遍歷時的前驅及後繼。然而,這樣會使儲存密度大大降低,而在有n個結點的二叉樹中有n+1個空鏈域,故可利用這些空鏈域來存放結點的前驅及後繼資訊,並作如下規定:若結點有左子樹,則其leftChild域指向其左孩子,否則,令leftChild指向其前驅;若結點有右子樹,則其rightChild域指向其右孩子,否則,令rightChild指向其後繼。

為了避免混淆,現對上章中的結點結構作適當改動,改動後如下:

leftChilt leftTag element rightTag rightChild

並規定: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");
        }
    }
}