JAVA实现二叉树
树是编程中一种常用的数据结构。以前在学习数据结构时,总想着如何实际地实现出一颗二叉树出来,现在参考了《数据结构与算法分析 JAVA语言描述 第二版》之后,照着书中的例子实现了一颗二叉树,个人感觉书上面的二叉树实现操作比较复杂。下面将我学到的一些知识记录下来:
1,定义树的操作的基本接口,其中不包括插入或删除操作,因为这二种操作与树的结构相关,不同的树的实现有着不同的插入与删除方式,故不应该将这二种操作放在接口中让所有的类来实现。同时,接口中也不包括遍历操作,因为并不是每个应用都会用到遍历。我们可以定义返回一个迭代器的方法,由于树中有多种不同的遍历,树的类可以含有几个方法,每个方法返回一种迭代器。
基本接口中定义了树的常用操作,代码如下:
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.html
TreeIteratorInterface<T> 的具体代码如下:
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; } }