二叉查找树是一棵二叉树,特别之处在于:对于树中的每个节点X,它的左子树中所有项的值小于X中的项,而它的右子树中所有项的值大于X中的项。
如果插入序列是随机的,则平均深度是O(logN)。
如果插入序列是预先排好序的,那么会退化成链表。
一种解决方案是有一个附加平衡条件,如AVL树,红黑树(red black tree);
另一种是每次操作之后使用一个规则调整平衡,如伸展树(splay tree)。
/**
* 二叉查找树
*
* 操作的平均时间为O(logN)
*
* 公有方法:
* void insert(T x);
* void remove(T x);
* T findMin();
* T findMax();
* boolean contains(T x);
* void makeEmpty();
* boolean isEmpty();
* void printTree();
*/
public class BinarySearchTree<T extends Comparable<? super T>> {
private BinaryNode<T> root; // 唯一数据域是对树根的引用
// 节点类
private static class BinaryNode<T> {
private T element;
private BinaryNode<T> left;
private BinaryNode<T> right;
BinaryNode(T theElement, BinaryNode<T> lt, BinaryNode<T> rt) {
element = theElement;
left = lt;
right = rt;
}
}
// 构造器
public BinarySearchTree() {
root = null;
}
// 插入,调用更general的私有插入方法
public void insert(T x) {
root = insert(x, root);
}
// 删除,调用更general的remove方法
public void remove(T x) {
root = remove(x, root);
}
// 最小项,为空返回null
public T findMin() {
if (isEmpty())
return null;
return findMin(root).element;
}
// 最大项,为空返回null
public T findMax() {
if (isEmpty())
return null;
return findMax(root).element;
}
// contains
public boolean contains(T x) {
return contains(x, root);
}
//
public void makeEmpty() {
root = null;
}
//
public boolean isEmpty() {
return root == null;
}
//
public void printTree() {
if (isEmpty()) {
System.out.println("Empty tree");
return;
} else
printTree(root);
}
private BinaryNode<T> insert(T x, BinaryNode<T> t) {
// 如果根为空
if (t == null)
return new BinaryNode<>(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);
return t;
}
private BinaryNode<T> remove(T x, BinaryNode<T> t) {
if (t == null)
return t; // Item not found; do nothing
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.left != null && t.right != 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;
}
// 只要有左儿子,就向左进行,这里用递归方式编程
private BinaryNode<T> findMin(BinaryNode<T> t) {
if (t == null)
return null;
else if (t.left == null)
return t;
return findMin(t.left);
}
// 只要有右儿子,就向右进行,这里用while循环代替递归
private BinaryNode<T> findMax(BinaryNode<T> t) {
if (t != null)
while (t.right != null)
t = t.right;
return t;
}
private boolean contains(T x, BinaryNode<T> t) {
if (t == null)
return false;
int compareResult = x.compareTo(t.element);
// 这里的递归调用可以用while循环代替
if (compareResult < 0)
return contains(x, t.left);
else if (compareResult > 0)
return contains(x, t.right);
else
return true; // Match
}
// 中序遍历print,结果是从小到大输出
private void printTree(BinaryNode<T> t) {
if (t != null) {
printTree(t.left);
System.out.println(t.element);
printTree(t.right);
}
}
// 计算树高度,本程序没用到
private int height(BinaryNode<T> t) {
if (t == null)
return -1;
else
return 1 + Math.max(height(t.left), height(t.right));
}
// 测试
public static void main(String[] args) {
BinarySearchTree<Integer> t = new BinarySearchTree<>();
for (int i = 0; i <= 100; i++) {
t.insert(i);
}
t.printTree();
System.out.println(t.findMax());
System.out.println(t.findMin());
System.out.println(t.contains(101));
}
}