樹是編程中一種經常使用的數據結構。之前在學習數據結構時,總想着如何實際地實現出一顆二叉樹出來,如今參考了《數據結構與算法分析 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; } }