个人博客:二叉查找树(Binary Sort Tree)的解析与实现
二叉查找树
关于性质之类的可以参考wiki、百度百科或者其他博客,其实也很简单,推荐看标准的描述。而博主写的内容主要是想写成类似Map底层的。所以有泛型,并且是Key-Value结构。关于二叉查找树,我们一般是类比二分搜索,仿佛跟Key-Value这样的结构并不一致。但是,在Java上,如果我们能在定义节点的时候,专门声明了Key与Value,并且在Key上定义comparator,那么就是通过对Key的排序,获取一个顺序序列,并且根据Key可以找到对应的Value。这也是Java容器TreeMap与TreeSet的底层是红黑树的原因(其实差不多啦,红黑树可以自平衡)
那么如果说红黑树是二叉查找树的升级版,因为红黑树是保证平衡的,也就可以理解为什么Map的底层是一棵红黑树。我们这样来定义树,也可以完成Key-Value的模式。从而支持高效查找。例如下图:
对于这些结点,编号就是Key,名字就是Value,如果设定Key的comparator,那么就按照编号来排序,在搜索某个编号的时候就能以O(logn)的复杂度搜索到对应的人的名字。
二叉查找树的定义与结点的定义
key与value是必须的,此外,想要同时对key定义一个comparator。由于Java语言,我们可以声明一个BST类,其中的内部类(或者可以专门写出来)可以有Node作为结点。那么可以这样来写:
class BST<Key extends Comparable<Key>, Value>{
private Node root;
private class Node{
private Key key;
private Value value;
private Node left;
private Node right;
private Node father;
public Node(Key key,Value value){
this.key = key;
this.value = value;
}
}
}
泛型是必要的,并且让Key继承Comparable,这样,我们在实际算法中,使用compareTo方法,就可以进行比较。使用的话,先new出来BST对象,然后调用例如put、get方法等等,这个后面再实现。
关于father结点,如果是递归的算法,father就可以不用了,在回溯的时候就可以处理。但是非递归的算法,虽然也可以用last等来代替,但是并不好,推荐使用father结点,非常方便。当然也是一个额外开销(但是真的微不足道)。
二叉查找树的查找
很简单,就跟二分一样,从根开始搜,如果比该结点的key小,那么则进入左子树,大则进入右子树。若相等,则表示搜索到了,若最终搜索到null仍未找到,那么就返回未搜索到。
自然,递归是非常简单明了的表现方式,但是递归的效率相对略低,但是比较容易理解。这里先写一个递归的写法:
public Value get(Key key){
if(key == null) return null;
return get(this.root,key);
}
public Value get(Node root,Key key){
if(root == null) return null;
int cmp = key.compareTo(root.key);
if(cmp == 0){
return root.value;
}else{
if(cmp < 0){
return get(root.left,key);
}else{
return get(root.right,key);
}
}
}
还是比较简单明了的,其实转化成非递归也非常容易:
public Value get(Key key){
if(key == null) return null;
return get(this.root,key);
}
public Value get(Node root,Key key){
while(root != null){
int cm