二分搜索树
二分搜索树的本质是树,对于树的基本知识,我在我的一片关于堆的博文的开头做了讲述,大家可以参考。这里将重点讲述树的递归结构。
定义
二分搜索树是一颗二叉树, 二分搜索树每个节点的左子树的值都小于该节点的值,每个节点右子树的值都大于该节点的值,其任意一个节点的每棵子树都满足二分搜索树的定义。NULL节点和只有一个节点的树也是二叉树。
理解
通过上面对二叉树的定义,我们知道了向树中存储的元素必须具有可比较性,我们在写代码的时候。向树中存储的是一个泛型为E
的元素,为了实现他的可比较性,所以要让他继承自Comparable
接口。
在这里简单说一下Comparable
接口,他下面只有CompareTo一个方法,这个方法是用于比较字符串顺序的,根据字典顺序进行排序。所有想要具有比较功能的类,都建议实现这个接口,而非是自己定义这个功能,这是面向对象的概念(将具有相同功能的事物抽象到一个共同的类或接口),并且为了多态也建议通过实现接口来进行向上转型,通过接口来操作具体实现,这也是面向接口编程要求我们做的。
好,我们言归正传,继续讲解二分搜索树,我这样的话就可以写出二分搜索树的底层定义了
private class Node{
public E e;
public Node left, right;
public Node (E e){
this.e = e;
left = null;
right = null;
}
}
将这个类设置为私有的内部类,用户不需要直接访问他,当我们在向其中插入元素e
的时候,需要判断他的根节点是否为空,若为空的话,直接将其赋值给根节点root = new Node(e)
,否则的话,进入递归循环,在这里需要判断递归的终止条件就是把元素e
放入到了树中,循环条件呢?循环条件是看e
的值比当前节点的值大还是小,大的话放入其右子树,小的话放入其左子树,这里分别调用的是add(node.right, e);
和add(node.left, e);
,这个add
方法调用的就是自己本身。
public void add(E e){
if (root == null){
root = new Node(e);
size ++;
}else{
add(root,e);
}
}
//递归插入元素e
public void add(Node node, E e){
//递归终止的条件
if (e.equals(node.e)){
return;
}else if (e.compareTo(node.e) < 0 && node.left == null ){
node.left = new Node(e);
size ++;
return;
}else if (e.compareTo(node.e) > 0 && node.right == null){
node.right = new Node(e);
size ++;
return;
}
if (e.compareTo(node.e) < 0){
add(node.left, e);
}else {
add(node.right, e);
}
}
在上面的代码中,我们发现在头节点的时候判断了他是否为空,在后面的递归中,也判断了插入的位置的节点是否为空,这样双重的判断很恼火,所以我们优化一下插入,对要插入的值直接调用add(root,e)
,此时它的返回值为root也就是返回插入新节点二分搜索树的根,
所以优化后算法的思路就是,当用户插入节点e
时,先在根节点判断是否为空,是的话就插入,返回一个Node对象,将其赋值给root,如果不为空的话,就就和当前的节点比较,如果小的话,就在当前节点的左子树中添加e,如果node.left
为空,那么就将当前的node.left
赋值为e
,因为它会重新调用本身,在第一个if循环返回了root。将其赋给了node.left
。关键是记住node是一个可变的。
public void add(E e) {
root = add(root,e);
}
//返回插入新节后二分搜索树的根
public Node add(Node node, E e) {
//递归终止的条件
if (node == null) {
size++;
return new Node(e);
}
if (e.compareTo(node.e) < 0) {
node.left = add(node.left, e);
} else if (e.compareTo(node.e) > 0){
node.right = add(node.right, e);
}
return node;
}
查找
通过对向树中插入数据的理解,我们可以很简单的写出在树中搜索元素的代码
public boolean contains (E e){
return contains(root, e);
}
private boolean contains(Node node, E e){
if (node == null){
return false;
}
if (e.compareTo(node.e) == 0) {
return true;
//要查找的值比当前节点的值小则和他的左子树比较
}else if (e.compareTo(node.e) < 0){
return contains(node.left,e);
}else {
return contains(node.right,e);
}
}