标签:
1. 引言
前些天数据结构课讲到了二叉树。学校使用的教材是《数据结构(Java版)(第4版,叶核亚)》。总觉得书中给出的二叉树(包括之前学的线性表)的实现方式不太“优雅”(面向对象),比如线性表的链式存储和实现一节中给出的SinglyList的插入方法:public Node<T> insert(int i, T t);比如在二叉树的链式存储和实现的插入方法:public BinaryNode<T> insert(BinaryNode<T> parent, T t, boolean leftChild);这些实现都把Node对象暴露了出去,但好的做法肯定是把它对使用者隐藏啊。我们在使用LinkedList时,有听说过Node对象吗?我们关心的是将我们需要组织的对象何时存入集合和取出集合,至于
集合内部是如何关联这些对象的,我们才不关心。但也可以理解书的做法,毕竟重点是数据结构而不是面向对象。
于是自己稍微改进了一下书中的二叉树的实现代码,让使用时更加简单方便。
2. 实现
一共有三个类:BinaryTree,BinaryTreeNode,Cursor。
1)BinaryTree对象。它代表一棵树,是直接面向使用者的。它封装了树的一些基本信息,如树根(root),游标(cursor),结点数(size),高度(height)等信息,还提供了一些方法,如获取,移动游标、获取结点数,高度,遍历树等。
2)BinaryTreeNode对象。它代表树的一个结点,是BinaryTree的静态内部类,对外透明。它封装的是一个结点的信息,如父结点(parent),左孩子结点(left),右孩子结点(right),结点的层次(level)。
3)Cursor对象。它也是BinaryTree的静态内部类,但对外公开。它表示的是一个指向树中某个结点的“指针”。使用者通过Cursor来操作树。如通过调用它的value()方法可获取当前树的Cursor指向的结点的值;调用value(T t)可修改值;调用child(T left, T right)可为当前指向的结点添加左右孩子。
下面给出Java实现代码:
import java.util.LinkedList;
/**
* 二叉树
*
* @author D.K
* @date 2015年10月14日 下午9:10:00
* @Description: TODO
*/
public class BinaryTree<T> {
public static final int ORDER_TYPE_PREORDER = 1;
public static final int ORDER_TYPE_INORDER = 2;
public static final int ORDER_TYPE_POSTORDER = 3;
public static final int ORDER_TYPE_LEVEL = 4;
private BinaryTreeNode<T> rootNode;
private Cursor<T> cursor;
private int size;
private int height;
public BinaryTree() {
}
public BinaryTree(T root) {
root(root);
size = 1;
height = 1;
}
/**
* 获取根元素
*
* @return
*/
public T root() {
if (isEmpty()) {
throw new RuntimeException("该树是空树!");
}
return rootNode.data;
}
/**
* 设置树的根元素(如果有,则替换)
*
* @param root
* @return
*/
public Cursor<T> root(T root) {
final BinaryTreeNode<T> newRootNode = new BinaryTreeNode<>(root, null, 1);
if (isEmpty())
cursor = new Cursor<>(this);
else {
newRootNode.left = rootNode.left;
newRootNode.right = rootNode.right;
}
rootNode = newRootNode;
cursor.node = rootNode;
return cursor;
}
/**
* 将游标指向到根元素
*
* @return true表示移动成功;否则表示移动失败(要移自的位置没有元素)。
*/
public boolean move2Root() {
if (!isEmpty()) {
cursor.node = rootNode;
return true;
}
return false;
}
/**
* 将游标指向到当前指向元素的父元素
*
* @return true表示移动成功;否则表示移动失败(要移自的位置没有元素)。
*/
public boolean move2Parent() {
if (cursor.node.parent != null) {
cursor.node = cursor.node.parent;
return true;
}
return false;
}
/**
* 将游标指向到当前指向元素的父元素的左侧相邻元素
*
* @return true表示移动成功;否则表示移动失败(要移自的位置没有元素)。
*/
public boolean move2ParentLeftNeighbor() {
return move2Parent() && move2LeftNeighbor();
}
/**
* 将游标指向到当前指向元素的父元素的右侧相邻元素
*
* @return true表示移动成功;否则表示移动失败(要移自的位置没有元素)。
*/
public boolean move2ParentRightNeighbor() {
return move2Parent() && move2RightNeighbor();
}
/**
* 将游标指向到当前指向元素的祖父元素
*
* @return true表示移动成功;否则表示移动失败(要移自的位置没有元素)。
*/
public boolean move2Grandparent() {
if (cursor.node.parent != null || cursor.node.parent.parent != null) {
cursor.node = cursor.node.parent.parent;
return true;
}
return false;
}
/**
* 将游标指向到当前指向元素的祖父元素的左侧相邻元素
*
* @return true表示移动成功;否则表示移动失败(要移自的位置没有元素)。
*/
public boolean move2GrandparentLeftNeighbor() {
return move2Grandparent() && move2LeftNeighbor();
}
/**
* 将游标指向到当前指向元素的祖父元素的右侧相邻元素
*
* @return true表示移动成功;否则表示移动失败(要移自的位置没有元素)。
*/
public boolean move2GrandparentRightNeighbor() {
return move2Grandparent() && move2RightNeighbor();
}
/**
* 将游标移动到当前指向元素的左孩子
*
* @return true表示移动成功;否则表示移动失败(要移自的位置没有元素)。
*/
public boolean move2LeftChild() {
if (cursor.node.left != null) {
cursor.node = cursor.node.left;
return true;
}
return false;
}
/**
* 将游标移动到当前指向元素相邻左侧的元素(可能是兄弟,也可能不是兄弟)
*
* @return
*/
public boolean move2LeftNeighbor() {
if (cursor.node == rootNode) {
return false;
}
if (cursor.isRightChild()) {
// 当前指向元素是其父元素的右孩子,这种情况很简单
if (cursor.node.parent.left != null) {
cursor.node = cursor.node.parent.left;
return true;
}
return false;
}
// 当前指向元素是其父元素的左孩子
// 创建临时移动的cursor
final Cursor<T> tempCursor = new Cursor<>(cursor);
final int level = tempCursor.level();
while (Cursor.isLeftChild(tempCursor.node.parent)) {
tempCursor.node = tempCursor.node.parent;
}
// 此时tempCursor指向元素的父元素是根元素或右孩子
if (tempCursor.node.parent == rootNode) {
return false;
}
// tempCursor移动到当前元素的祖父元素的左孩子
tempCursor.node = tempCursor.node.parent.parent.left;
while (tempCursor.node.right != null && tempCursor.node.right.level != level) {
tempCursor.node =