二叉搜索树(Binary Search Tree)
定义:二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为 BST
性质:
- 任何一个节点的值都大于其左子树所有节点的值
- 任意一个节点的值都小于其右子树所有节点的值
- 它的左右子树也是一棵二叉搜索树
- 二叉搜索树可以大大提高搜索数据的效率
- 二叉搜索树存储的元素必须具备可比较性
- 比如 int、double 等
- 如果是自定义类型,需要指定比较方式
- 不允许为 null
二叉搜索树的接口设计
- 二叉树没有索引的概念
int size() // 元素的数量
boolean isEmpty() //是否为空
void clear() //清空所有元素
void add(E element) //添加元素
void remove(E element) //删除元素
boolean contains(E element)//是否包含某元素
接口的实现
添加节点
步骤:
- 找到父节点parent
- 创建新节点
- parent.left = node || parent.right = node
- 如果是相同值,覆盖旧的值
实现代码:
//添加节点
public void add(E element){
elementNotNullCheck(element);
//添加第一个节点
if (root == null){
root = new Node<E>(element, null);
size++;
return;
}
//添加非第一个节点
//找到父节点
Node<E> parent = root;
Node<E> node = root;
int cmp = 0;
while(node != null){
cmp = compare(element, node.element);
parent = node;//保存新添加节点的父节点是谁
if(cmp > 0){
node = node.right;
} else if (cmp < 0){
node = node.left;
} else { //相等
node.element = element;//新元素覆盖老元素
return;
}
}
//添加到父节点的哪个位置
Node<E> newNode = new Node<E>(element, parent);
if(cmp > 0){
parent.right = newNode;
} else {
parent.left = newNode;
}
size++;
}
元素之间的比较设计
- 允许外界传入一个Comparator自定义比较方案
- 如果没有传入Comparator,强制认为元素已经实现了Compareable接口
代码:
//comparator字段
private Comparator<E> comparator;
//二叉搜索树的构造器
public BinarySearchTree(){
this(null);
}
public BinarySearchTree(Comparator<E> comparator){
this.comparator = comparator;
}
/**
* 节点值比较方法
* 返回值等于0,e1 == e2;
* 返回值大于0,e1大于e2;
* 返回值小于0,e1小于e2.
*/
private int compare(E e1, E e2){
if (comparator != null){
return comparator.compare(e1,e2);
}
return ((Comparable<E>)e1).compareTo(e2);
}
根据元素内容获取节点
//获取元素对应的节点
private Node<E> node(E element){
Node<E> node = root;
while (node != null){
int cmp = compare(element, node.element);
if (cmp == 0) return node;
if (cmp > 0) {
node = node.right;
} else {
node = node.left;
}
}
return null;
}
删除节点
情况有四种:
- remove节点是叶子节点
直接删除
若 node = node.parent.left;
则 node.parent.left = null;
若 node = node.parent.right;
则 node.parent.right = null;
若 node.parent = null;
则 node.parent.left = null;
- remove节点是度为1的节点
用子节点替代原节点的位置
设child是node.left或者child是node.right
用child替代node的位置
- 如果node是左子节点
则 child.parent = node.parent
node.parent.left = child- 如果node是右子节点
则 child.parent = node.parent
node.parent.right = child- 如果node是根节点
则 root = child
child.parent = null
- remove节点是度为2的节点
用前驱或者后继节点的值覆盖原节点的值
然后删除相应的前驱或者后继节点
注意:如果一个节点的度为2,那它的前驱、后继节点的度只可能是1 || 0
- remove节点是根节点
//移除节点
public void remove(E element){
remove(node(element));
}
private void remove(Node<E> node){
if (node == null) return;
size --;
//删除度为2的节点
if (node.hasTwoChildren()){
//找到node的后继节点
Node<E> s = successor(node);
//将后继节点的值赋值给node节点
node.element = s.element;
//删除后继节点
node = s;
}
//删除度为1或者为0的节点
Node<E> replacement = node.left != null ? node.left : node.right;
if (replacement != null) {//node是度为1的节点
//更改parent
replacement.parent = node.parent;
//更改node的left||right指向
if (node.parent == null) {//node是度为1的节点并且是根节点
root = replacement;
} else if (node == node.parent.right){//node是父节点的右节点
node.parent.right = replacement;
} else {//node是父节点的左节点
node.parent.left = replacement;
}
} else if (node.parent == null) { //node是叶子节点并且是根节点
root = null;
} else { //node是叶子节点但不是根节点
if (node == node.parent.right){
node.parent.right = null;
} else {
node.parent.left = null;
}
}
}