二叉搜索树
- 二叉搜索树的概念
- 手搓二叉搜索树
-
- MyBinarySearchTree的成员变量和构造函数
- MyBinarySearchTree的辅助函数
- [MyBinarySearchTree查找元素:search( )](#MyBinarySearchTree查找元素:search( ))
- MyBinarySearchTree插入元素:insert ()
- [MyBinarySearchTree删除指定元素:remove( )⭐](#MyBinarySearchTree删除指定元素:remove( )⭐)
- 完整测试
二叉搜索树的概念
手搓二叉搜索树
MyBinarySearchTree的成员变量和构造函数
java
public TreeNode root; // 树的根节点
// 静态内部类:节点结构
static class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
MyBinarySearchTree的辅助函数
辅助类:包装节点及其父节点
java
// 辅助类:包装节点及其父节点,方便返回多个值
public class NodePair {
public TreeNode node1; // 目标节点
public TreeNode node2; // 父节点
public NodePair(TreeNode node1, TreeNode node2) {
this.node1 = node1;
this.node2 = node2;
}
}
辅助方法:在给定节点的右子树中寻找最小值(最左节点)
java
// 辅助方法:在给定节点的右子树中寻找最小值(最左节点)
private NodePair findLeftmostNode(TreeNode cur) {
TreeNode leftmostNode = cur.right;
TreeNode leftmostNodeParent = cur;
if (leftmostNode == null) return new NodePair(null, cur);
while (leftmostNode.left != null) {
leftmostNodeParent = leftmostNode;
leftmostNode = leftmostNode.left;
}
return new NodePair(leftmostNode, leftmostNodeParent);
}
辅助方法:在给定节点的左子树中寻找最大值(最右节点)
java
// 辅助方法:在给定节点的左子树中寻找最大值(最右节点)
private NodePair findRightmostNode(TreeNode cur) {
TreeNode rightmostNode = cur.left;
TreeNode rightmostNodeParent = cur;
if (rightmostNode == null) return new NodePair(null, cur);
while (rightmostNode.right != null) {
rightmostNodeParent = rightmostNode;
rightmostNode = rightmostNode.right;
}
return new NodePair(rightmostNode, rightmostNodeParent);
}
辅助方法:交换两个节点的值
java
// 交换两个节点的值
private void swapNodeVal(TreeNode node1, TreeNode node2) {
int tmp = node1.val;
node1.val = node2.val;
node2.val = tmp;
}
MyBinarySearchTree查找元素:search( )
java
/**
* 查找方法
* @param key 要查找的值
* @return 找到的节点,若没找到则返回 null
*/
public TreeNode search(int key) {
TreeNode cur = root;
while (cur != null) {
if (cur.val == key) {
return cur; // 找到目标值
} else if (cur.val < key) {
cur = cur.right; // 目标值大,向右走
} else {
cur = cur.left; // 目标值小,向左走
}
}
return null;
}
MyBinarySearchTree插入元素:insert ()
java
/**
* 插入方法
* @param key 要插入的值
*/
public void insert(int key) {
// 情况 1:树为空,直接作为根节点
if (root == null) {
root = new TreeNode(key);
return;
}
// 情况 2:树不为空,寻找插入位置
TreeNode cur = root;
TreeNode curParent = null; // 需要记录父节点,否则找到位置后无法连接
while (cur != null) {
if (cur.val == key) {
// BST 通常不包含重复 key,若存在则直接返回
return;
} else if (cur.val < key) {
curParent = cur;
cur = cur.right;
} else {
curParent = cur;
cur = cur.left;
}
}
// 此时 cur 为空,curParent 是叶子节点
TreeNode newNode = new TreeNode(key);
if (curParent.val > key) {
curParent.left = newNode; // 比父节点小,连在左边
} else {
curParent.right = newNode; // 比父节点大,连在右边
}
}
MyBinarySearchTree删除指定元素:remove( )⭐
java
/**
* 删除方法
* @param key 要删除的值
*/
public void remove(int key) {
if (root == null) {
System.out.println("树为空!");
return;
}
// 1. 寻找要删除的节点及其父节点
TreeNode cur = root;
TreeNode parent = null;
while (cur != null && cur.val != key) {
parent = cur;
if (key < cur.val) {
cur = cur.left;
} else {
cur = cur.right;
}
}
if (cur == null) {
System.out.println("没有找到!");
return;
}
// 2. 开始删除逻辑
// 情况 A:删除叶子节点(左右都为空)
if (cur.left == null && cur.right == null) {
if (cur == root) {
root = null; // 删的是根节点且它是唯一节点
} else if (parent.left == cur) {
parent.left = null;
} else {
parent.right = null;
}
}
// 情况 B:删除只有左子树的节点
else if (cur.right == null) {
if (cur == root) {
root = cur.left;
} else if (parent.left == cur) {
parent.left = cur.left; // 父节点接手孙子节点
} else {
parent.right = cur.left;
}
}
// 情况 C:删除只有右子树的节点
else if (cur.left == null) {
if (cur == root) {
root = cur.right;
} else if (parent.left == cur) {
parent.left = cur.right;
} else {
parent.right = cur.right;
}
}
// 情况 D:删除有两个子树的节点(最复杂的情况)
else {
// 采用"替换法":找到右子树中的最小值(最左节点)来顶替当前位置
NodePair pair = findLeftmostNode(cur);
TreeNode leftmostNode = pair.node1;
TreeNode leftmostNodeParent = pair.node2;
// 将右子树最小节点的值覆盖到当前要删除的节点上
swapNodeVal(cur, leftmostNode);
// 现在问题转化为删除那个"最左节点"
// 最左节点一定没有左孩子,但可能有右孩子
if (leftmostNodeParent.left == leftmostNode) {
leftmostNodeParent.left = leftmostNode.right;
} else {
// 特殊情况:cur.right 就是最左节点时(即 cur.right 没有左子树)
leftmostNodeParent.right = leftmostNode.right;
}
}
}
完整测试
java
public class TestBST {
public static void main(String[] args) {
MyBinarySearchTree bst = new MyBinarySearchTree();
// 1. 测试插入
int[] data = {50, 30, 70, 20, 40, 60, 80, 35};
System.out.println("--- 插入测试 ---");
for (int val : data) {
bst.insert(val);
}
System.out.print("当前树(中序遍历,应有序): ");
bst.inorderTraversal(); // 期待输出: 20 30 35 40 50 60 70 80
// 2. 测试查找
System.out.println("\n--- 查找测试 ---");
int searchKey = 40;
MyBinarySearchTree.TreeNode found = bst.search(searchKey);
System.out.println("查找 " + searchKey + (found != null ? ": 成功" : ": 失败"));
searchKey = 100;
found = bst.search(searchKey);
System.out.println("查找 " + searchKey + (found != null ? ": 成功" : ": 失败"));
// 3. 测试删除:叶子节点 (20)
System.out.println("\n--- 删除测试:叶子节点 (20) ---");
bst.remove(20);
bst.inorderTraversal(); // 期待: 30 35 40 50 60 70 80
// 4. 测试删除:只有一个子节点的节点 (40)
// 此时 40 的左边是 35
System.out.println("\n--- 删除测试:单分支节点 (40) ---");
bst.remove(40);
bst.inorderTraversal(); // 期待: 30 35 50 60 70 80
// 5. 测试删除:有两个子节点的节点 (70)
// 70 的左边是 60,右边是 80
System.out.println("\n--- 删除测试:双子树节点 (70) ---");
bst.remove(70);
bst.inorderTraversal(); // 期待: 30 35 50 60 80
// 6. 测试删除:根节点 (50)
System.out.println("\n--- 删除测试:根节点 (50) ---");
bst.remove(50);
bst.inorderTraversal(); // 期待: 30 35 60 80
// 7. 测试删除不存在的节点
System.out.println("\n--- 删除测试:不存在的节点 (99) ---");
bst.remove(99);
}
}