一. 什么是二叉搜索树?
二叉搜索树是一棵特殊的二叉树, 又称为二叉排序树, 其特性为:
- 空树也是二叉搜索树
- 若它的左子树不为空, 则左子树上所有节点值均小于根节点的值
- 若它的右子树不为空, 则右子树上所有节点值均大于根节点的值
- 二叉搜索树的左右子树也都是二叉搜索树
二叉搜索树通过中序遍历可以得到一个有序的序列
例如上图中, 中序遍历的结果为: 2 3 3 7 8 9 10 12 16 18
二. 关于二叉搜索树的操作
1. 增加一个节点
根据二叉搜索树的性质, 增加节点的思路如下:
- 假设在这棵二叉搜索树中, 不存在两个值相同的节点
- 要想插入一个节点, 就需要和二叉搜索树中的其他节点值进行比较, 从而找到其应该插入的地方
- 如果这个值 < 当前节点值, 就和当前节点的左子树进行比较
- 如果这个值 > 当前节点值, 就和当前节点的右子树进行比较
- 每次进行比较时, 都记录下父节点的位置
public class BSTree {
public static class Node {
public int val;
public Node left;
public Node right;
public Node(int val) {
this.val = val;
}
}
public Node root;
public boolean insertNode(int val) {
Node node = new Node(val);
// 如果二叉搜索树为空, 就将新插入的节点作为根节点并返回
if(root == null) {
root = node;
return true;
}
Node cur = root;
Node parent = null; // 表示 cur 节点的父节点
while (cur != null) {
if(cur.val == val) {
return false; // 二叉搜索树中不存在值相同的节点
}
if(cur.val > val) {
parent = cur;
cur = cur.left;
} else {
parent = cur;
cur = cur.right;
}
}
if(parent.val > val) {
parent.left = node;
} else {
parent.right = node;
}
return true;
}
}
2. 查找一个节点值
根据二叉搜索树的性质:
如果当前节点值 < 待查找值, 去该节点右子树继续查找
如果当前节点值 > 待查找值, 去该节点左子树继续查找
public Node search(int val) {
Node cur = root;
while (cur != null) {
if(cur.val == val) {
return cur;
} else if(cur.val > val){
cur = cur.left;
} else {
cur = cur.right;
}
}
return null;
}
3. 删除一个节点的值
先要查找到待删除节点的位置, 然后再进行删除
定义 cur 为待删除的元素, 定义 parent 为待删除元素的父节点
删除步骤需要分成以下三种情况:
- 如果 cur.left == null (要删除的节点没有左子树)
(1). 如果 cur == root, 则 root == cur.right;
(2). 如果cur 不为 root, 但 cur == parent. left, 则 parent. left = cur.right;
(3) 如果 cur 不为 root, 但 cur == parent. right, 则 parent. right = cur. right; - 如果 cur.right == null (要删除的节点没有右子树)
(1). 如果 cur == root, 则 root == cur. left;
(2). 如果cur 不为 root, 但 cur == parent. left, 则 parent. left = cur.left;
(3) 如果 cur 不为 root, 但 cur == parent. right, 则 parent. right = cur. left; - 如果 cur.left != null && cur. right != null (要删除的节点左右子树均存在)
需要使用替换法进行删除, 将要删除的元素值替换成左子树的最大值或右子树的最小值, 这时只需要删除左子树最大值或右子树最小值即可(删除的情况不是cur.right == null 情况就是 cur.left == null 情况)
public boolean removeNode(Node parent, Node cur) {
if(cur.left == null) {
if(cur == root) {
root = cur.right;
} else if(cur == parent.left) {
parent.left = cur.right;
} else {
parent.right = cur.right;
}
} else if(cur.right == null) {
if(cur == root) {
root = cur.left;
} else if(cur == parent.left) {
parent.left = cur.left;
} else {
parent.right = cur.left;
}
} else {
// cur.left != null && cur.right != null
Node target = cur.right; // 寻找要替换的节点
Node targetP = cur;
while (target.left != null) {
targetP = target;
target = target.left;
}
cur.val = target.val; // 此时target一定走到了右树的最左边, target 对应的节点值为右子树的最小值
if(target == targetP.left) {
targetP.left = target.right;
} else {
targetP.right = target.right;
}
}
return true;
}
// 删除二叉搜索树中的某个节点
public boolean remove(int val) {
Node cur = root; // cur 表示要删除的节点
Node parent = null; // parent 表示要删除节点的父节点
// 找到要删除的节点位置
while (cur != null) {
if(cur.val == val) {
return removeNode(parent, cur); // 要删除节点的位置
} else if(cur.val > val) {
parent = cur;
cur = cur.left;
} else {
parent = cur;
cur = cur.right;
}
}
return false;
}
3. 二叉搜索树的性能
最好情况下, 二叉搜索树是一棵完全二叉树, 它搜索节点的时间复杂度为: O(logn);
最坏情况下, 二叉搜索树会是一棵只有单分支的树, 则它搜索节点的时间复杂度为: O(n)