JAVA實現二叉樹

樹是編程中一種經常使用的數據結構。之前在學習數據結構時,總想着如何實際地實現出一顆二叉樹出來,如今參考了《數據結構與算法分析 JAVA語言描述 第二版》以後,照着書中的例子實現了一顆二叉樹,我的感受書上面的二叉樹實現操做比較複雜。下面將我學到的一些知識記錄下來:html

1,定義樹的操做的基本接口,其中不包括插入或刪除操做,由於這二種操做與樹的結構相關,不一樣的樹的實現有着不一樣的插入與刪除方式,故不該該將這二種操做放在接口中讓全部的類來實現。同時,接口中也不包括遍歷操做,由於並非每一個應用都會用到遍歷。咱們能夠定義返回一個迭代器的方法,因爲樹中有多種不一樣的遍歷,樹的類能夠含有幾個方法,每一個方法返回一種迭代器。java

基本接口中定義了樹的經常使用操做,代碼以下:node

public interface TreeInterface<T> {
    public T getRootData();
    public int getHeight();
    public int getNumberOfNodes();
    public boolean isEmpty();
    public void clear();
}

 

2,因爲對樹的許多操做都少不了遍歷,所以須要構造一個可以對樹中的元素進行遍歷的迭代器。本例中採用內部類的方式來實現迭代器,下面的定義的接口 TreeIteratorInterface 包含了生成不一樣迭代器的方法。讓實現樹的具體的JAVA類 implements TreeIteratorInterface<T>,而後該JAVA類再定義一個內部類 implements Iterator<T>,便可構造一個可以對樹中元素進行遍歷的迭代器。算法

爲何要讓實現樹的具體的JAVA類 implements TreeIteratorInterface<T>?編程

由於,TreeIteratorInterface<T>接口中定義了返回各類各樣迭代器的方法,實現樹的具體的JAVA類 的對象就能夠調用這些方法得到迭代器了。在後面的例子中,咱們定義了一個具體實現樹數據結構的類 BinaryTree<T>,該類 implements BinaryTreeInterface<T>,而 BinaryTreeInterface<T> 又extends TreeIteratorInterface<T>,於是BinaryTree<T>的對象能夠調用 TreeIteratorInterface<T>接口中的方法來得到迭代器了。數據結構

關於如何爲自定義的數據結構實現迭代器,一個更好的參考例子:http://www.cnblogs.com/hapjin/p/4454594.htmlide

TreeIteratorInterface<T> 的具體代碼以下:post

public interface TreeIteratorInterface<T> {
    public Iterator<T> getPreorderIterator();
    public Iterator<T> getPostorderIterator();
    public Iterator<T> getInorderIterator();
    public Iterator<T> getLevelOrderIterator();
}

 

3,定義了樹的接口以後,因爲咱們大部分狀況仍是使用二叉樹,所以下面定義二叉樹的接口。首先,二叉樹也是樹,所以繼承了TreeInterface<T>;其次,二叉樹的對象要可以得到迭代器,所以又繼承了TreeIteratorInterface<T>。(JAVA中接口容許多重繼承)學習

該二叉樹接口沒有什麼特別的操做(大部分基本操做已經從TreeInterface<T>中繼承下來了),它只定義了兩個方法,這兩個方法用來生成二叉樹。測試

//JAVA接口能夠多繼承
public interface BinaryTreeInterface<T> extends TreeInterface<T> ,TreeIteratorInterface<T>{
    //如下這兩個方法定義瞭如何構造二叉樹
    public void setTree(T rootData);//構造一顆以rootData爲根的二叉樹
    //構造一顆以rootData爲根,leftTree爲左子樹,rightTree爲右子樹的二叉樹
    public void setTree(T rootData, BinaryTreeInterface<T> leftTree, BinaryTreeInterface<T> rightTree);
}

 

4,與單鏈表同樣,鏈表中有鏈表結點,二叉樹中也須要定義表示結點的類。而這裏定義的表示結點的類採用獨立的外部類實現,而不是採用內部類來實現。

在考慮表示結點的類時,咱們先定義了一個抽象的接口來規定對結點的一些基本操做。(這也是爲何我以爲書中二叉樹實現比較複雜的緣由)

該接口爲 BinaryNodeInterface<T>,具體代碼以下:

//二叉樹結點的接口
public interface BinaryNodeInterface<T> {
    public T getData();//返回結點的數據部分
    public void setData(T newData);//設置結點的數據域的值
    
    public BinaryNodeInterface<T> getLeftChild();//獲取結點的左孩子
    public BinaryNodeInterface<T> getRightChild();//獲取結點的右孩子
    
    public void setLeftChild(BinaryNodeInterface<T> leftChild);//設置結點的左孩子爲指定結點
    public void setRightChild(BinaryNodeInterface<T> rightChild);//設置結點的右孩子爲指定結點
    
    public boolean hasLeftChild();//判斷結點是否有左孩子
    public boolean hasRightChild();//判斷結點是否有右孩子
    
    public boolean isLeaf();//檢查結點是不是葉子結點
    
    public int getNumberOfNodes();//計算以該結點爲根的子樹的結點數目
    public int getHeight();//計算以該結點爲根的子樹的高度
    public BinaryNodeInterface<T> copy();//複製以該結點爲根的子樹
}

 

5,接下來即是二叉樹的節點的實現類了,該類 implements BinaryNodeInterface<T> 從而實現了接口中定義的各類對節點的操做。

class BinaryNode<T> implements BinaryNodeInterface<T>, java.io.Serializable{
    private T data;//結點的數據域
    private BinaryNode<T> left;//左孩子
    private BinaryNode<T> right;//右孩子
    
    public BinaryNode(){
        this(null);
    }
    //構造一個值爲dataPortaion的結點
    public BinaryNode(T dataPortion){
        this(dataPortion, null, null);
    }
    
    public BinaryNode(T dataPortion, BinaryNode<T> leftChild, BinaryNode<T> rightChild){
        data = dataPortion;
        left = leftChild;
        right = rightChild;
    }
    
    
    @Override
    //返回結點的數據域的值
    public T getData() {
        return data;
    }

    @Override
    //更改結點數據域的值
    public void setData(T newData) {
        data = newData;
        
    }

    @Override
    //得到結點的左孩子
    public BinaryNodeInterface<T> getLeftChild() {
        return left;
    }

    @Override
    //得到結點的右孩子
    public BinaryNodeInterface<T> getRightChild() {
        return right;
    }

    @Override
    //更改結點的左孩子
    public void setLeftChild(BinaryNodeInterface<T> leftChild) {
        left = (BinaryNode<T>)leftChild;
    }

    @Override
    //更改結點的右孩子
    public void setRightChild(BinaryNodeInterface<T> rightChild) {
        right = (BinaryNode<T>)rightChild;
    }

    @Override
    public boolean hasLeftChild() {
        return left != null;
    }

    @Override
    public boolean hasRightChild() {
        return right != null;
    }

    @Override
    public boolean isLeaf() {
        return (left == null) && (right == null);
    }

    @Override
    //返回以該結點爲根的子樹中的結點的個數(包括根結點)
    public int getNumberOfNodes() {
        int leftNumber = 0;
        int rightNumber = 0;
        if(left != null)
            leftNumber = left.getNumberOfNodes();
        if(right != null)
            rightNumber = right.getNumberOfNodes();
        return 1 + leftNumber + rightNumber;
    }

    @Override
    //返回以此結點爲根的子樹的高度
    public int getHeight() {
        return getHeight(this);
    }
    
    private int getHeight(BinaryNode<T> node){
        int height = 0;
        if(node != null)
            height = 1 + Math.max(getHeight(node.left), getHeight(node.right));
        return height;
    }

    @Override
    //該方法被構造二叉樹的setTree()方法調用
    public BinaryNodeInterface<T> copy() {
        BinaryNode<T> newRoot = new BinaryNode<T>(data);
        if(left != null)
            newRoot.left = (BinaryNode<T>)left.copy();
        if(right != null)
            newRoot.right = (BinaryNode<T>)right.copy();
        return newRoot;
    }
}

 

6,二叉樹的節點也定義好了,是時候實現二叉樹了(好麻煩啊啊啊)。上面已經談到,BinaryTree<T>實現了一顆具體的二叉樹,而且經過定義了一個內部類來生成迭代器。這裏就簡要介紹下這個實現迭代器的內部類:該內部類名爲 private class InorderIterator<T> implements Iterator<T>

這裏,進行了一些偷懶,即下面的代碼中只實現了可以對樹進行中序遍歷的迭代器即InorderIterator。固然了,你還能夠再定義一個內部類PreorderIterator,而後按照先序遍歷採用迭代的方法來實現先序遍歷的迭代器。

下面正式介紹私有內部類InorderIterator<T>,因爲它 implements Iterator<T>,所以須要實現Iterator<T>中定義的三個抽象方法,這三個方法就是用來完成迭代的(遍歷的)。咱們按照中序遍歷(非遞歸方式)的邏輯來實現這三個方法。中序遍歷(非遞歸方式)須要棧來輔助存儲結構,所以,代碼中定義了一個順序棧來保存遍歷過程當中遇到的結點,而該順序棧的實現請參考另外一篇博文:http://www.cnblogs.com/hapjin/p/4442729.html

實現二叉樹的BinaryTree<T>代碼以下:

import java.util.Iterator;
import java.util.NoSuchElementException;

import list.SequenceStack;//SequenceStack在list包中
import list.Stack;//Stack接口在list包中

public class BinaryTree<T> implements BinaryTreeInterface<T>, java.io.Serializable{

    private BinaryNodeInterface<T> root;//樹根結點

    private class InorderIterator<T> implements Iterator<T>{

        //定義一個順序棧nodeStack來存放遍歷過程當中遇到的結點
        private Stack<BinaryNodeInterface<T>>nodeStack;//list包中有順序棧的實現
        private BinaryNodeInterface<T> currentNode;
        public InorderIterator(){
            nodeStack = new SequenceStack<BinaryNodeInterface<T>>();
            currentNode = (BinaryNodeInterface<T>) root;//此處爲何須要強制轉換呢?
        }
        
        /*
         * 按照中序遍歷的邏輯進行實現Iterator接口中的方法,從而實現一個迭代器
         */
        @Override
        public boolean hasNext() {
            return (!nodeStack.empty()) || (currentNode != null);
        }

        @Override
        public T next() {
            BinaryNodeInterface<T> nextNode = null;
            while(currentNode != null){
                nodeStack.push(currentNode);
                currentNode = currentNode.getLeftChild();
            }
            if(!nodeStack.empty()){
                nextNode = nodeStack.pop();
                assert nextNode != null;
                currentNode = nextNode.getRightChild();
            }
            else
                throw new NoSuchElementException();
            return nextNode.getData();
        }

        @Override
        //僅僅是完成遍歷的功能,刪除功能是不須要有的。
        public void remove() {
            throw new UnsupportedOperationException();
        }
        
    }
    
    public BinaryTree(){
        root = null;
    }
    public BinaryTree(T rootData){
        root = new BinaryNode<T>(rootData);
    }
    public BinaryTree(T rootData, BinaryTree<T> leftTree, BinaryTree<T> rightTree){
        privateSetTree(rootData, leftTree, rightTree);
    }
    
    @Override
    public void setTree(T rootData) {
        root = new BinaryNode<T>(rootData);
    }

    @Override
    /*
     *以rootData爲根,leftTree爲左子樹,rightTree爲右子樹
     *生成一顆新的二叉樹,setTree()實際調用了privateSetTree()來構造二叉樹
     */
    public void setTree(T rootData, BinaryTreeInterface<T> leftTree,
            BinaryTreeInterface<T> rightTree) {
            privateSetTree(rootData, (BinaryTree)leftTree, (BinaryTree)rightTree);
    }
    private void privateSetTree(T rootData, BinaryTree<T>leftTree, BinaryTree<T> rightTree){
        root = new BinaryNode<T>(rootData);
        if((leftTree != null) && (!leftTree.isEmpty()))
            root.setLeftChild(leftTree.root);
        if((rightTree != null) && (!rightTree.isEmpty())){
            if(rightTree != leftTree)
                root.setRightChild(rightTree.root);
            else
                root.setRightChild(rightTree.root.copy());
        }
        if((leftTree != null) && (leftTree != this))
            leftTree.clear();
        if((rightTree != null) && (rightTree != this))
            rightTree.clear();    
    }
    
    //更改根結點的數據域
    protected void setRootData(T rootData){
        root.setData(rootData);
    }
    
    //更改根結點
    protected void setRootNode(BinaryNodeInterface<T> rootNode){
        root = rootNode;
    }
    protected BinaryNodeInterface<T> getRootNode(){
        return root;
    }
    
    @Override
    //返回樹的根節點的數據域
    public T getRootData() {
        T rootData = null;
        if(root != null)
            rootData = root.getData();//調用節點的getData(),返回該節點的數據域
        return rootData;
    }

    @Override
    //返回二叉樹的高度
    public int getHeight() {
        return root.getHeight();//二叉樹的高度即爲以根結點爲根的子樹的高度
    }

    @Override
    //返回二叉樹中結點的個數
    public int getNumberOfNodes() {
        return root.getNumberOfNodes();
    }

    @Override
    public boolean isEmpty() {
        return root == null;
    }

    @Override
    public void clear() {
        root = null;
    }

    //中序遍歷二叉樹
    public void inorderTraverse(){
        inorderTraverse(root);
    }
    
    private void inorderTraverse(BinaryNodeInterface<T> node){
        if(node != null){
            inorderTraverse((BinaryNode)node.getLeftChild());
            System.out.println(node.getData());//若使用迭代器,能夠在測試程序中輸出,而不是在這裏使用輸出語句
            inorderTraverse((BinaryNode)node.getRightChild());
        }
    }
    
    public void preorderTraverse(){
        preorderTraverse(root);
    }
    
    private void preorderTraverse(BinaryNodeInterface<T> node){
        if(node != null){
            System.out.println(node.getData());
            preorderTraverse((BinaryNode)node.getLeftChild());
            preorderTraverse((BinaryNode)node.getRightChild());
        }
    }
    
    public void postorderTraverse(){
        postorderTraverse(root);
    }
    private void postorderTraverse(BinaryNodeInterface<T> node){
        if(node != null){
            postorderTraverse((BinaryNode)node.getLeftChild());
            postorderTraverse((BinaryNode)node.getRightChild());
            System.out.println(node.getData());
        }
    }
    @Override
    //得到先序遍歷器的方法,因爲在該類中沒有定義生成先序迭代器的私有內部類,所以該方法爲空實現
    public Iterator<T> getPreorderIterator() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    //得到後序遍歷器的方法,因爲在該類中沒有定義生成後序迭代器的私有內部類,所以該方法爲空實現
    public Iterator<T> getPostorderIterator() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Iterator<T> getInorderIterator() {
        return new InorderIterator();
    }

    @Override
    //得到層次遍歷器的方法,因爲在該類中沒有定義生成層次迭代器的私有內部類,所以該方法爲空實現
    public Iterator<T> getLevelOrderIterator() {
        // TODO Auto-generated method stub
        return null;
    }
}
相關文章
相關標籤/搜索