目录
1.概念及其特性
二叉搜索树:
1.是一个二叉树
2.每个节点中保存关键字(Key)
3.关键字需要具备比较能力
4.每个节点遵守:左子树的所有Key < 其key <右子树的所有Key
5.二叉树中不会出现相等的Key
6.二叉搜索树的中序遍历一定是有序的
2.基本操作
我们需要一个结点类和一个二叉搜索树类
结点类Node:
public class Node {
public Integer key;
public Node left;
public Node right;
public Node(Integer key) {
this.key = key;
}
@Override
public String toString() {
return "Node{" +
"key=" + key +
'}';
}
}
二叉搜索树类:
public class BST {
private Node root;
}
(1).查找
1.方法体分析:
2.思路:
查找的时候,比当前节点小就往左走,比当前节点大就往右走,相等就退出,找到null也退出,具体代码如下:
public boolean search(Integer key){
Node current = this.root;
while (current != null){
int cmp = key.compareTo(current.key);
if (cmp == 0){
return true;
}else if (cmp < 0){
current = current.left;
}else {
current = current.right;
}
}
return false;
}
(2).插入
如果一个方法中需要用到对象,则这个方法一定是非静态的。
1.方法体分析:
2.代码思路:
和查找类似,设一个parent结点,维护其一直是cur的双亲结点,然后cur往下找,要是找到相等的结点,则抛出异常;找到空,就可以进行插入(要根据cmp判断一下是插到左边还是右边),具体代码如下:
public void insert(Integer key) {
if (root == null) {
root = new Node(key);
return;
}
// 始终保持 parent 就是 current 的双亲节点
Node parent = null;
Node current = root;
int cmp = 0;
while (current != null) {
cmp = key.compareTo(current.key);
if (cmp == 0) {
throw new RuntimeException("BST 中不允许出现相同的 key");
} else if (cmp < 0) {
parent = current;
current = current.left;
} else {
parent = current;
current = current.right;
}
}
// current 一定 null。
// parent 就是要插入新节点的双亲节点
Node node = new Node(key);
if (cmp < 0) {
parent.left = node;
} else {
parent.right = node;
}
}
(3).删除
1.分析
2.代码思路
1).查找包含key的结点,记作node。node的父节点记作parent
2).判断
情况一:node.left == null && node.right == null
情况二:node.left != null && node.right == null
情况三:和情况二类似
情况四:替换法(选择左子树中最大的一个节点(节点的right == null,记为ghost))
node是要删除的那个节点,ghost是左子树中最大的那个节点
具体代码如下:
public boolean remove(Integer key) {
// 1. 查找要删除的 key 所在的节点,记作 node。node 的双亲节点,记作 parent
Node parent = null;
Node current = root;
while (current != null) {
int cmp = key.compareTo(current.key);
if (cmp == 0) {
removeInternal(current, parent);
return true;
} else if (cmp < 0) {
parent = current;
current = current.left;
} else {
parent = current;
current = current.right;
}
}
return false;
}
private void removeInternal(Node node, Node parent) {
if (node.left == null && node.right == null) {
if (node == root) {
root = null;
} else if (node == parent.left) {
parent.left = null;
} else {
parent.right = null;
}
} else if (node.left != null && node.right == null) {
if (node == root) {
root = node.left;
} else if (node == parent.left) {
parent.left = node.left;
} else {
parent.right = node.left;
}
} else if (node.left == null && node.right != null) {
if (node == root) {
root = node.right;
} else if (node == parent.left) {
parent.left = node.right;
} else {
parent.right = node.right;
}
} else {
// 使用替换法删除,使用 node 的左子树中的最大值所在的节点,记作 ghost。ghost 的双亲记作 ghostParent
Node ghostParent = node;
Node ghost = node.left;
// 一路朝右查找,直到 ghost.right == null
while (ghost.right != null) {
ghostParent = ghost;
ghost = ghost.right;
}
//这里的ghost一定没有右子树
// 1. 替换
node.key = ghost.key;
// 2. 删除 ghost 节点(其右孩子一定是 null)
if (node == ghostParent) {
//删除点即为左子树最大点的双亲,只有这种情况,ghost才有可能为ghostParent的左子树
//这种情况就是node的左孩子没有右孩子
ghostParent.left = ghost.left;
} else {
//否则,ghost一定是ghostParent右子树
//这种情况就是node的右孩子没有左孩子
ghostParent.right = ghost.left;
}
}
}
3.总结
二叉搜索树
重要特点:中序是有序的!
操作:
1.插入/查找/删除
2.时间复杂度:最好和平均的:O(log(n)) 最坏:O(n)