我们在写TreeSet和TreeMap的时候,都知道它们的底层是二叉树,那下面我们就一起看看用Java代码实现二叉树,并实现二叉树的插入,查找,获取最大值,最小值等功能.
/*
* 二叉树类:泛型T代表是可以比较的类
*/
public class BinarySearchTree<T extends Comparable<? super T>> {
/*
* 内部类:代表节点类
*/
private static class BinaryNode<T>{
T element; //数据
BinaryNode<T> left; //左子树
BinaryNode<T> right;//右子树
public BinaryNode(T theElement){
this(theElement,null,null);
}
public BinaryNode(T element, BinaryNode<T> left, BinaryNode<T> right) {
this.element = element;
this.left = left;
this.right = right;
}
}
private BinaryNode<T> root; //节点属性
public BinarySearchTree() {
root = null; //第一次创建的时候是空树
}
/*
* 删除树的所有元素
*/
public void makeEmpty(){
root = null;
}
/*
* 判断树是否为空
*/
public boolean isEmpty(){
return root == null;
}
/*
* 判断树是否包含这个元素
*/
public boolean contains(T x){
return contains(x,root);
}
/*
* 查找最小数
*/
public T findMin(){
if(isEmpty())
throw new NullPointerException(); //为空时,抛出空指针异常
return findMin(root).element;
}
/*
* 查找最大数
*/
public T findMax(){
if(isEmpty())
throw new NullPointerException(); //为空时,抛出空指针异常
return findMax(root).element;
}
/*
* 插入数据
*/
public void insert(T x){
root = insert(x,root);
}
/*
* 删除数据
*/
public void remove(T x){
root = remove(x, root);
}
/*
* contains:如果树中包含项x的节点,就返回true,否则返回false.
* 树的结构使的这种操作很简单.如果t是空集,就返回false,否则,如果存储在t出的项是x,
* 那么久返回true.否则我么对树t的左子树或右子树进行一次递归的调用,这依赖于x与存储在t中的项的关系
*/
private boolean contains(T x, BinaryNode<T> t){
if(t == null)
return false;
//两个数进行比较,大于零就遍历右子树,小于零就遍历左子树,等于零表示t包含x
int compareResult = x.compareTo(t.element);
if(compareResult < 0)
return contains(x,t.left);
else if(compareResult > 0)
return contains(x, t.right);
else
return true;
}
/*
* 找最小元素,只要从根开始并且只要有左儿子就向左进行.终止点就是最小的元素
* 利用递归写.
*/
private BinaryNode<T> findMin(BinaryNode<T> t){
if(t == null)
return null;
else if(t.left == null)
return t;
return findMin(t.left);
}
/*
* 找最大元素,只要从根开始并且只要有右儿子就向右进行.终止点就是最大的元素
* 利用非递归写.如果想用递归写就参考求最小元素的方法.
*/
private BinaryNode<T> findMax(BinaryNode<T> t){
if(t != null){
while(t.right != null)
t = t.right;
}
return t;
}
/*
* 插入元素:写插入元素的时候,可以像用contains方法一样沿着树查找,如果找到x,就什么都不用做
* 否则,将x插入到遍历的路径上的最后一点上.
* 由于该t引用该树的根,而根又在第一次插入的时时变化,
*/
private BinaryNode<T> insert(T x, BinaryNode<T> t) {
if(t == null)
return new BinaryNode<T>(x,null,null); //为空时,创建该节点,并且该节点的左右子树都为空
int compareResult = x.compareTo(t.element);
if(compareResult < 0)
t.left = insert(x,t.left);
else if(compareResult > 0)
t.right = insert(x,t.right);
else
; //相等就什么都不用处理
return t;
}
/*
* 删除:删除操作可以说是最困难的了.一旦我们发现要被删除的节点,就要考虑几种可能的情况.
* 1.如果节点是一片树叶,那么它可以被立即删除
* 2.如果节点有一个儿子,则该节点可以在其父节点调整自己的链以绕过该节点后被删除
* 3.如果节点有两个儿子,也就是最复杂的情况.一般的删除策略是用其右子树的最小的数据代替该节点的数据并递归地删除那个节点.因为右
* 子树中的最小节点不可能有左儿子,所以第二次remove要容易
*/
private BinaryNode<T> remove(T x, BinaryNode<T> t) {
if(t == null)
return t;
int compareResult = x.compareTo(t.element);
if(compareResult < 0)
t.left = remove(x, t.left);
else if(compareResult > 0)
t.right = remove(x,t.right);
else if(t.right!=null && t.left!=null){
t.element = findMin(t.right).element;
t.right = remove(t.element,t.right);
}else
t = (t.left != null)?t.left : t.right;
return t;
}
}
然后自己可以写个测试自己测试一下.我已在代码下上了注释.