博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JAVA实现二叉树
阅读量:7048 次
发布时间:2019-06-28

本文共 10254 字,大约阅读时间需要 34 分钟。

树是编程中一种常用的数据结构。以前在学习数据结构时,总想着如何实际地实现出一颗二叉树出来,现在参考了《数据结构与算法分析 JAVA语言描述 第二版》之后,照着书中的例子实现了一颗二叉树,个人感觉书上面的二叉树实现操作比较复杂。下面将我学到的一些知识记录下来:

1,定义树的操作的基本接口,其中不包括插入或删除操作,因为这二种操作与树的结构相关,不同的树的实现有着不同的插入与删除方式,故不应该将这二种操作放在接口中让所有的类来实现。同时,接口中也不包括遍历操作,因为并不是每个应用都会用到遍历。我们可以定义返回一个迭代器的方法,由于树中有多种不同的遍历,树的类可以含有几个方法,每个方法返回一种迭代器。

基本接口中定义了树的常用操作,代码如下:

public interface TreeInterface
{ 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
{ public Iterator
getPreorderIterator(); public Iterator
getPostorderIterator(); public Iterator
getInorderIterator(); public Iterator
getLevelOrderIterator();}

 

3,定义了树的接口之后,由于我们大部分情况还是使用二叉树,因此下面定义二叉树的接口。首先,二叉树也是树,因此继承了TreeInterface<T>;其次,二叉树的对象要能够获得迭代器,因此又继承了TreeIteratorInterface<T>。(JAVA中接口允许多重继承)

该二叉树接口没有什么特别的操作(大部分基本操作已经从TreeInterface<T>中继承下来了),它只定义了两个方法,这两个方法用来生成二叉树。

//JAVA接口可以多继承public interface BinaryTreeInterface
extends TreeInterface
,TreeIteratorInterface
{ //以下这两个方法定义了如何构造二叉树 public void setTree(T rootData);//构造一颗以rootData为根的二叉树 //构造一颗以rootData为根,leftTree为左子树,rightTree为右子树的二叉树 public void setTree(T rootData, BinaryTreeInterface
leftTree, BinaryTreeInterface
rightTree);}

 

4,与单链表一样,链表中有链表结点,二叉树中也需要定义表示结点的类。而这里定义的表示结点的类采用独立的外部类实现,而不是采用内部类来实现。

在考虑表示结点的类时,我们先定义了一个抽象的接口来规定对结点的一些基本操作。(这也是为什么我觉得书中二叉树实现比较复杂的原因)

该接口为 BinaryNodeInterface<T>,具体代码如下:

//二叉树结点的接口public interface BinaryNodeInterface
{ public T getData();//返回结点的数据部分 public void setData(T newData);//设置结点的数据域的值 public BinaryNodeInterface
getLeftChild();//获取结点的左孩子 public BinaryNodeInterface
getRightChild();//获取结点的右孩子 public void setLeftChild(BinaryNodeInterface
leftChild);//设置结点的左孩子为指定结点 public void setRightChild(BinaryNodeInterface
rightChild);//设置结点的右孩子为指定结点 public boolean hasLeftChild();//判断结点是否有左孩子 public boolean hasRightChild();//判断结点是否有右孩子 public boolean isLeaf();//检查结点是否是叶子结点 public int getNumberOfNodes();//计算以该结点为根的子树的结点数目 public int getHeight();//计算以该结点为根的子树的高度 public BinaryNodeInterface
copy();//复制以该结点为根的子树}

 

5,接下来便是二叉树的节点的实现类了,该类 implements BinaryNodeInterface<T> 从而实现了接口中定义的各种对节点的操作。

class BinaryNode
implements BinaryNodeInterface
, java.io.Serializable{ private T data;//结点的数据域 private BinaryNode
left;//左孩子 private BinaryNode
right;//右孩子 public BinaryNode(){ this(null); } //构造一个值为dataPortaion的结点 public BinaryNode(T dataPortion){ this(dataPortion, null, null); } public BinaryNode(T dataPortion, BinaryNode
leftChild, BinaryNode
rightChild){ data = dataPortion; left = leftChild; right = rightChild; } @Override //返回结点的数据域的值 public T getData() { return data; } @Override //更改结点数据域的值 public void setData(T newData) { data = newData; } @Override //获得结点的左孩子 public BinaryNodeInterface
getLeftChild() { return left; } @Override //获得结点的右孩子 public BinaryNodeInterface
getRightChild() { return right; } @Override //更改结点的左孩子 public void setLeftChild(BinaryNodeInterface
leftChild) { left = (BinaryNode
)leftChild; } @Override //更改结点的右孩子 public void setRightChild(BinaryNodeInterface
rightChild) { right = (BinaryNode
)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
node){ int height = 0; if(node != null) height = 1 + Math.max(getHeight(node.left), getHeight(node.right)); return height; } @Override //该方法被构造二叉树的setTree()方法调用 public BinaryNodeInterface
copy() { BinaryNode
newRoot = new BinaryNode
(data); if(left != null) newRoot.left = (BinaryNode
)left.copy(); if(right != null) newRoot.right = (BinaryNode
)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
implements BinaryTreeInterface
, java.io.Serializable{ private BinaryNodeInterface
root;//树根结点 private class InorderIterator
implements Iterator
{ //定义一个顺序栈nodeStack来存放遍历过程中遇到的结点 private Stack
>nodeStack;//list包中有顺序栈的实现 private BinaryNodeInterface
currentNode; public InorderIterator(){ nodeStack = new SequenceStack
>(); currentNode = (BinaryNodeInterface
) root;//此处为什么需要强制转换呢? } /* * 按照中序遍历的逻辑进行实现Iterator接口中的方法,从而实现一个迭代器 */ @Override public boolean hasNext() { return (!nodeStack.empty()) || (currentNode != null); } @Override public T next() { BinaryNodeInterface
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
(rootData); } public BinaryTree(T rootData, BinaryTree
leftTree, BinaryTree
rightTree){ privateSetTree(rootData, leftTree, rightTree); } @Override public void setTree(T rootData) { root = new BinaryNode
(rootData); } @Override /* *以rootData为根,leftTree为左子树,rightTree为右子树 *生成一颗新的二叉树,setTree()实际调用了privateSetTree()来构造二叉树 */ public void setTree(T rootData, BinaryTreeInterface
leftTree, BinaryTreeInterface
rightTree) { privateSetTree(rootData, (BinaryTree)leftTree, (BinaryTree)rightTree); } private void privateSetTree(T rootData, BinaryTree
leftTree, BinaryTree
rightTree){ root = new BinaryNode
(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
rootNode){ root = rootNode; } protected BinaryNodeInterface
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
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
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
node){ if(node != null){ postorderTraverse((BinaryNode)node.getLeftChild()); postorderTraverse((BinaryNode)node.getRightChild()); System.out.println(node.getData()); } } @Override //获得先序遍历器的方法,由于在该类中没有定义生成先序迭代器的私有内部类,因此该方法为空实现 public Iterator
getPreorderIterator() { // TODO Auto-generated method stub return null; } @Override //获得后序遍历器的方法,由于在该类中没有定义生成后序迭代器的私有内部类,因此该方法为空实现 public Iterator
getPostorderIterator() { // TODO Auto-generated method stub return null; } @Override public Iterator
getInorderIterator() { return new InorderIterator(); } @Override //获得层次遍历器的方法,由于在该类中没有定义生成层次迭代器的私有内部类,因此该方法为空实现 public Iterator
getLevelOrderIterator() { // TODO Auto-generated method stub return null; }}

 

你可能感兴趣的文章
org.dom4j.DocumentException:对实体 "virtual_card_id" 的引用必须以 ';' 分隔符结尾
查看>>
【ASP.NET MVC系列】浅谈ASP.NET 页面之间传值的几种方式
查看>>
linux内核剖析(七)Linux进程间通信的几种方式总结
查看>>
180510.最近踩过和听过的sql的坑
查看>>
api 25 PopupWindow会占据整个屏幕
查看>>
Target runtime Apache Tomcat v6.0 is not defined.错误解决方法
查看>>
Android GreenDao操作外部DB数据库文件
查看>>
spring 邮件服务
查看>>
饿了么多活利器:实时双向复制工具(DRC)
查看>>
SpringBoot各类扩展点详解
查看>>
NancyFx-打造小型 WebAPI 與 Microservice 的輕巧利器
查看>>
为什么使用Reazor
查看>>
使用intellij idea打包并部署到外部的tomcat
查看>>
jz2440存储管理实验【学习笔记】
查看>>
SQL Server 关于列的权限控制
查看>>
springboot - Constructor、@Autowired、@PostConstruct分析
查看>>
InnoDB recovery过程解析
查看>>
WPF 中动态创建和删除控件
查看>>
springboot+mysql实现quartz集群搭建
查看>>
docker 构建 https 私有仓库 Registry
查看>>