二叉搜索树(Binary Search Tree,简称BST)是一种特殊的二叉树数据结构,它满足以下特性:
-
有序性:对于树中的任意一个节点,其左子树中所有节点的值都小于该节点的值,而其右子树中所有节点的值都大于该节点的值。这意味着,对二叉搜索树进行中序遍历(先遍历左子树,再访问根节点,最后遍历右子树)会得到一个递增的有序序列。
-
结构特性:每个节点最多有两个子节点,通常称为左子节点和右子节点。没有父节点的节点称为根节点,而没有子节点的节点称为叶子节点。
二叉搜索树的这些性质使得它特别适合用于动态查找表的实现,支持几个关键操作:
-
查找:从根节点开始,根据目标值与当前节点值的比较结果,决定是继续在左子树还是右子树中查找,直到找到目标值或遇到空节点为止。由于每次比较都可以排除一半的剩余节点,查找的平均时间复杂度为O(log n),其中n是树中节点的数量。
-
插入:新节点总是作为叶子节点被添加。从根节点开始,沿着与新节点值的比较路径向下,直到遇到空的子节点位置,然后在此位置插入新节点。
-
删除:删除操作相对复杂,需要考虑多种情况,如被删除节点是否有子节点,删除后需要保持二叉搜索树的性质。处理要删除的节点有两个子节点的情况时,可以找到右子树的最小值节点来替换待删除节点,然后将其删除;或者找到左子树的最大节点来替换待删除节点,然后将其删除。如下图中,加入待删除节点是10,则可以用9或者15来顶10,核心是保持BST的有序特性。
代码实现
package org.example.code;
/**
* @author Mazai-Liu
* @time 2024/6/22
*/
public class BST {
public static void main(String[] args) {
BST tree = new BST();
tree.insert(50);
tree.insert(30);
tree.insert(20);
tree.insert(40);
tree.insert(70);
tree.insert(60);
tree.insert(80);
System.out.println("Inorder traversal of the given tree:");
tree.traversal();
System.out.println("\n\nDelete 20");
tree.delete(20);
System.out.println("Inorder traversal after deletion:");
tree.traversal();
System.out.println("\n\nSearching for 20: " + (tree.search(20) != null ? "Found" : "Not Found"));
}
// 树节点定义
static class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
// BST的根节点
private TreeNode root;
// 搜索值
public TreeNode search(int val) {
return doSearch(root, val);
}
// 插入值
public void insert(int key) {
root = doAdd(root, key);
}
// 删除
public void delete(int key) {
// 用左子树的最大节点、右子树的最小节点来顶
root = doDelete(root, key);
}
// 遍历
public void traversal() {
doTraversal(root);
}
// 根据有序的特性进行查找
private TreeNode doSearch(TreeNode root, int val) {
if(root == null || root.val == val)
return root;
return root.val > val ? doSearch(root.left, val) : doSearch(root.right, val);
}
private TreeNode doAdd(TreeNode root, int val) {
if(root == null)
return new TreeNode(val);
if(root.val > val)
root.left = doAdd(root.left, val);
else
root.right = doAdd(root.right, val);
return root;
}
// 中序遍历
private void doTraversal(TreeNode root) {
if(root == null)
return;
doTraversal(root.left);
System.out.println(root.val);
doTraversal(root.right);
}
// 删除节点并更新中间节点的引用情况
private TreeNode doDelete(TreeNode root, int key) {
if(root == null)
return root;
if(root.val > key)
root.left = doDelete(root.left, key);
else if(root.val < key)
root.right = doDelete(root.right, key);
else {
// 没左或没右可以直接用另一边来顶了
if (root.right == null)
return root.left;
if (root.left == null)
return root.right;
// 找右子树的最小节点来顶
// TreeNode theNode = root.right;
// while (theNode.left != null) {
// theNode = theNode.left;
// }
// root.val = theNode.val;
// root.right = doDelete(root.right, theNode.val);
// 找左子树的最大节点来顶
TreeNode theNode = root.left;
while (theNode.right != null) {
theNode = theNode.right;
}
root.val = theNode.val;
// 记得删除用来顶的那个元素
root.left = doDelete(root.left, theNode.val);
}
return root;
}
}