BST是什么
⼆叉搜索树(Binary Search Tree,简称 BST)是⼀种很常见的的⼆叉树。 每个节点都有两个分支,一个左节点和一个右节点。而且,所有左子树的节点都比父节点小,所有右子树的节点都比父节点大。所以,如果你要在这里找一个数字,你只需要从根节点开始逐个比较就可以了。
BST注意事项
首先,要求数据必须是可比较的,也就是说,你要能够比较两个数据的大小。 其次,有些时候可能会退化成一条链,比如输入的数据是按照从小到大排列的,那么我就只有右子树,而左子树都是空的。
另外还有一些小技巧,比如说,如果你要删除一个节点,可以把它的后继节点(也就是比它大的最小节点)代替它,这样可以避免破坏整个树的结构。
代码实践
判断合法二叉树
思路
要判断一棵树是不是合法的BST,需要满足以下条件:
- 对于树中的每个节点,其左子树中的所有节点都小于该节点,右子树中的所有节点都大于该节点。
- 对于树中的每个节点,其左子树和右子树都满足上述条件。
为了实现这些条件,可以使用递归的方式来遍历树。假设当前遍历到的节点是node,我们需要检查node的值是否大于其左子树中的所有节点,小于其右子树中的所有节点,并且左子树和右子树都是合法的BST。如果不满足任何一个条件,则说明该树不是合法的BST。
在BST中,对于每个节点,其左子树中的所有节点都小于该节点,右子树中的所有节点都大于该节点。因此,我们需要知道每个节点可以取的最小和最大值,以便在递归中进行比较和判断。 对于任何一个节点,它的最小值为它的左子树的最大值,而最大值为它的右子树的最小值。因此,在递归遍历树的过程中,我们需要不断更新每个节点的最小值和最大值,以确保树的所有节点都满足BST的条件。
代码
/**
* 判断BST是否合法
* @param root
* @return
*/
public static boolean isValidBST(TreeNode root) {
return isValid(root, null, null);
}
private static boolean isValid(TreeNode root, TreeNode min, TreeNode max) {
if (root == null){
return true;
}
if (null != min && root.val <= min.val || null != max && root.val >= max.val) {
return false;
}
return isValid(root.left, min, root) && isValid(root.right, root, max);
}
查找BST的某个节点是否存在
思路
graph TD
根节点是否为空 --> 为空返回false
根节点是否为空 --> 不为空 --> 根节点的值等于需要查找的值返回true
不为空 --> 查找的值大于根节点的值 --> 递归地查找右子树
不为空 --> 查找的值小于根节点的值 --> 递归地查找左子树
代码
/**
* 判断target是否存在于root
* @param target
* @param root
* @return
*/
public static boolean exist(int target, TreeNode root) {
if (null == root) {
return false;
}
if (target == root.val) {
return true;
} else if (target > root.val) {
return exist(target, root.right);
} else {
return exist(target, root.left);
}
}
插入
思路
graph TD
根节点是否为空 --> 为空 --> 将新节点插入到根节点的位置上并返回新节点
根节点是否为空 --> 不为空
不为空 --> 新节点的值小于根节点的值 --> 递归调用插入方法将新节点插入到左子树中
不为空 --> 新节点的值大于根节点的值 --> 递归调用插入方法将新节点插入到右子树中
代码
/**
* 插入
* @param newNode
* @param root
* @return
*/
public static TreeNode insert(TreeNode newNode, TreeNode root) {
if (root == null) {
return newNode;
}
if (newNode.val < root.val) {
root.left = insert(newNode, root.left);
} else {
root.right = insert(newNode, root.right);
}
return root;
}
删除
思路
- 如果根节点为空,则直接返回null。
- 如果要删除的节点的值等于根节点的值,则执行以下操作:
- 如果根节点的左子树为空,则返回右子树作为新的根节点。
- 如果根节点的右子树为空,则返回左子树作为新的根节点。
- 否则,找到根节点右子树中最小的节点,将该节点的值赋给根节点,然后在右子树中递归删除该最小节点。
- 如果要删除的节点的值小于根节点的值,则在左子树中递归查找要删除的节点并进行删除。
- 如果要删除的节点的值大于根节点的值,则在右子树中递归查找要删除的节点并进行删除。
- 最后返回新的根节点。
代码
/**
* 删除
* @param deleteNode
* @param root
* @return
*/
public static TreeNode delete(TreeNode deleteNode, TreeNode root) {
if (null == root) {
return null;
}
if (deleteNode.val == root.val) {
if (root.left == null) {
return root.right;
} else if (root.right == null) {
return root.left;
} else {
TreeNode minNode = getMin(root.right);
root.val = minNode.val;
root.right = delete(root, root.right);
}
} else if (deleteNode.val < root.val) {
root.left = delete(deleteNode, root.left);
} else {
root.right = delete(deleteNode, root.right);
}
return root;
}
private static TreeNode getMin(TreeNode root) {
while (root.left != null) {
root = root.left;
}
return root;
}