二叉搜索树概念
二叉搜索树 : 满足 BST 性质
任意节点的关键码 >= 左子树所有节点的关键码
任意节点的关键码 <= 右子树所有节点的关键码
中序遍历【必然有序】。二叉搜索树操作的时间复杂度 为 O(h)--> h 为 树高。 最坏一条链,即二叉搜索树在极端情况下会退化为一个链表。
BST 检索问题
- 从根递归查找
- 根值 大于 val ,递归检索左子树
- 根值 小于 val ,递归检索右子树。
- 不存在 检索为NULL 的情况。
代码[代码随想录]
// 递归,利用二叉搜索树特点,优化
public TreeNode searchBST(TreeNode root, int val) {
if (root == null || root.val == val) {
return root;
}
if (val < root.val) {
return searchBST(root.left, val);
} else {
return searchBST(root.right, val);
}
}
二叉搜索树插入数据的问题(其实就是检索问题 找对应位置插入)
- 插入相同元素,节点应包含相同节点数量。
- 插入不同元素,在对应位置插入关键码为 val 的结点。
二叉搜索树求前驱问题
前驱 : BST 中 小于 val 的最大节点
给定 val , 两种情况
- val存在 : 检索右子树一直走到头
- val 不存在,前驱在所有检索过程中经过的节点中,可以用变量存储一下祖先
二叉搜索树求后继问题
后继 : BST 中 大于 val 的最小节点
求法与 前驱类似
BST 删除问题
基于检索和求后继节点(前驱)实现。
检索 val : 1、 若 val 只有一颗子树**【直接删除】**,子树与父亲连接即可。若两颗子树,找到后继,先删除后继,再用后继节点代替val的位置。
删除根节点示例
实战
二叉搜索树的插入操作
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
// 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
if (root == null) return new TreeNode(val);
if (val < root.val) {
root.left = insertIntoBST(root.left,val);
}
else root.right = insertIntoBST(root.right,val);
return root;
}
}
后继者
class Solution {
int ans = Integer.MAX_VALUE;
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
// 找二叉搜索树的后继节点 基于检索
if (root == null) return null;
// 检索到对应节点
if (root.val == p.val) {
// 后继 : 比 p.val 大的节点的最小值
TreeNode next = p.right;
if (next == null ) {
if (ans == Integer.MAX_VALUE) return null;
return new TreeNode(ans);
}
else {
// next.left != null next 永不为 null
while (next.left != null) next = next.left;
return next;
}
}
// 递归检索 根大
if (root.val < p.val) { return inorderSuccessor(root.right,p);}
else {ans = Math.min(ans,root.val);return inorderSuccessor(root.left,p);}
}
}
- 总结 : 花费时间有点长。 检索到 val 的位置 , 具体卡壳 : 关键记录祖先代码
ans = Math.min(ans,root.val);由于 二叉搜索树的性质、【左小】【右大】 在 else 检索代码中,Math.min 一路记录了 【比他大的最小祖先】 即为后继 、当且仅当当此节点没有右节点时
二叉搜索树的删除操作
- 思路 :二叉树的删除操作 可以基于 检索 + 找后继实现 具体看上面的图。
- 两种情况分别是:这种重复的事情 我们可以交给递归 来完成。
- 被删节点只有一棵子树,该节点直接替换被删除节点。完成调整
- 被删节点名下有两棵子树,找【被删节点后继】 替换 【被删节点】 完成删除。
代码
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
// 递归到死 都没有 与之匹配的
if (root == null) return null;
if (root.val == key) {
// 找到了删除节点 是否 没有子树? case 1
if (root.left == null && root.right == null) return null;
if (root.left == null) return root.right;
if (root.right == null) return root.left;
// 后继节点 找最大的最小的 我们发现 走到这里 next 用不为空 hh 少了一步 记录祖先的操作
TreeNode next = root.right;
while (next.left != null) next = next.left;
// 删除 后继节点~
root.right = deleteNode(root.right,next.val);
// 替换 root 节点的值
root.val = next.val;
return root;
}
// 检索 二叉搜索树的代码 + 找后继 删除要接住
if (key < root.val) root.left = deleteNode(root.left , key);
else root.right = deleteNode(root.right,key);
return root;
}
}