本文将会介绍一种能够将链表插入的灵活性和有序数组查找的高效性结合在一起的符号表实现。
定义
一颗二叉查找树(BST)是一颗二叉树,其中每个结点都含有一个Comparable的键(以及相关联的值)且每个结点的键都大于其左子树的任意结点的键而小于任意右结点的键。
其实现代码如下(采用泛型编写):
public class BST<Key extends Comparable<Key>,Value>{
private Node root;//根节点
private class Node{
private Key key;
private Value val;
private Node left,right;
private int N;
public Node(Key key,Value val,int N){
this.key = key;
this.val = val;
this.N = N;
}//构造方法
}
//求二叉树大小
public int size(){
return size(root);
}
private int size(Node x){
if(x==null) return 0;
else return x.N;
}
//二叉树查找
public Value get(Key key){
return get(root,key);
}
private Value get(Node x,Key key){
if(x==null) return null;
int cmp = key.compareTo(x.key);
if(cmp<0) return get(x.left,key);
else if(cmp>0) return get(x.right,key);
else return x.val;
}
//二叉树插入
public void put(Key key,Value val){
root = put(root,key,val);
}
private Node put(Node x,Key key,Value val){
if(x==null) return new Node(key,val,1);
int cmp = key.compareTo(x.key);
if(cmp < 0) x.left = put(x.left,key,val);
else if(cmp > 0) x.right = put(x.right,key,val);
else x.val = val;//键存在,则更新值
x.N = size(x.left) + size(x.right) + 1;
return x;
}
//寻找最小键
public Key min(){
return min(root).key;
}
private Node min(Node x){
if(x==null) return x;
return min(x.left);
}
//取整
public Key floor(Key key){
Node x = floor(root,key);
if(x==null) return null;
return x.key;
}
private Node floor(Node x,Key key){
if(x==null) return null;
int cmp = key.compareTo(x.key);
if(cmp==0) return x;
if(cmp<0) return floor(x.left,key);
Node t = floor(x.right,key);
if(t!=null) return t;
else return x;
}
//返回小于key值的键的数量
public int rank(Key key){
return rank(key,root);
}
private int rank(Key key,Node x){
if(x==null) return 0;
int cmp = key.compareTo(x.key);
if(cmp<0) return rank(key,x.left);
else if(cmp>0) return 1+size(x.left)+rank(key,x.right);
else return size(x.left);
}
//删除最小结点
public void deleteMin(){
root = deleteMin(root);
}
private Node deleteMin(Node x){
if(x.left==null) return x.right;//返回作为新的根节点
x.left = deleteMin(x.left);//递归删除,返回之后作为新的left子树
x.N = size(x.left)+size(x.right)+1;
return x;
}
//删除操作,删除结点之后,使用右子树中的最小结点填补
public void delete(Key key){
root = delete(root,key);
}
public Node delete(Node x,Key key){
if(x==null) return null;
int cmp = key.compareTo(x.key);
if (cmp<0) x.left=delete(x.left,key);//调用递归
else if(cmp>0) x.right=delete(x.right,kry);
else{
if(x.right==null) return x.left;
if(x.left==null) return x.right;
Node t =x;
x = min(x.right);//使用右子树的最小结点代替
x.right = deteleMin(t.right);
x.left = t.left;
}
x.N = size(x.left) + size(x.right) + 1;
return x;
}
}
其中使用了很多递归,递归与非递归的区别是,递归的实现更容易验证其正确性,而非递归的实现效率更高。
二叉查找树的效率取决于树的高度,树的高度决定了算法在最坏情况下的性能,因此还可以对其进行优化为平衡二叉树,它能保证在无论键的插入顺序如何,树的高度都是总键数的对数。