235. 二叉搜索树的最近公共祖先
力扣链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/
之前236二叉树的最近公共祖先这道题的回溯法一样可以给出答案。
但是这道题中是二叉搜索树 - 也就是是一颗有序的树。因此可以考虑采用二叉搜索树的特性来解决问题。
在有序树里,如果判断一个节点的左子树里有p,右子树里有q呢?
因为是有序树,所有 如果 中间节点是 q 和 p 的公共祖先,那么 中节点的数组 一定是在 [p, q]区间的。即 中节点 > p && 中节点 < q 或者 中节点 > q && 中节点 < p。
那么只要从上到下去遍历,遇到 cur节点是数值在[p, q]区间中则一定可以说明该节点cur就是q 和 p的公共祖先。
问题: 一定是最近公共祖先吗?
我们从根节点搜索,第一次遇到 cur节点是数值在[p, q]区间中,即 节点5,此时可以说明 p 和 q 一定分别存在于 节点 5的左子树,和右子树中。
此时节点5是不是最近公共祖先? 如果 从节点5继续向左遍历,那么将错过成为q的祖先, 如果从节点5继续向右遍历则错过成为p的祖先。
所以当我们从上向下去递归遍历,第一次遇到 cur节点是数值在[p, q]区间中,那么cur就是 p和q的最近公共祖先。
因此可以采用递归法来解决这个问题:
递归三部曲:
确定返回参数以及参数 - TreeNode; (TreeNode cur, TreeNode p, TreeNode q)
确定终止条件 - 遇到空节点返回 (其实都不需要这个终止条件,因为题目中说了p、q 为不同节点且均存在于给定的二叉搜索树中。也就是说一定会找到公共祖先的,所以并不存在遇到空的情况。)
确定单层递归逻辑 - 如果cur.var > p.var&q.var,说明公共祖先在左边 -- cur.left;如果cur.var < p.var&q.var,说明公共祖先在右边 -- cur.right;如果正好cur在中间,那么返回cur
注意:这里有返回值,所以需要采用if+return的方式进行递归
具体代码实现:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
return helper(root, p, q);
}
public TreeNode helper(TreeNode cur, TreeNode p, TreeNode q){
if(cur == null){
return cur;
}
if(cur.val > p.val && cur.val > q.val){
TreeNode left = helper(cur.left, p, q);
return left;
}
if(cur.val < p.val && cur.val < q.val){
TreeNode right = helper(cur.right, p, q);
return right;
}
return cur;
}
}
参考资料:https://programmercarl.com/0235.二叉搜索树的最近公共祖先.html#递归法
701. 二叉搜索树中的插入操作
力扣链接:https://leetcode.cn/problems/insert-into-a-binary-search-tree/
这道题不用给它想得太复杂(要去改变二叉树的结构之类的),直接根据二叉搜索树的特性,在叶子节点处插入二叉树就可以。
因此可以采用递归法进行实现:
递归三部曲:
输出以及输入参数:TreeNode root; (TreeNode cur, int var)
终止条件:if(cur = null) 插入节点并且返回
单层循环:如果var比cur小,向左遍历;如果var比cur大,向右遍历。
注意:如何通过递归函数返回值完成了新加入节点的父子关系赋值操作了,下一层将加入节点返回,本层用root->left或者root->right将其接住。
if(root.val > val) root.left = insertIntoBST(root.left, val);
if(root.val < val) root.right = insertIntoBST(root.right, val);
如果不用的话,那么返回的结果就不存在树的结构了。
具体代码实现:
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root == null){
TreeNode node = new TreeNode(val);
return node;
}
if(root.val > val) root.left = insertIntoBST(root.left, val);
if(root.val < val) root.right = insertIntoBST(root.right, val);
return root;
}
参考资料:https://programmercarl.com/0701.二叉搜索树中的插入操作.html#递归
450.删除二叉搜索树中的节点
力扣链接:https://leetcode.cn/problems/delete-node-in-a-bst/
删除二叉搜索树的节点要比添加复杂很多。
因为有很多种情况:
没有找到要删除的节点。
删除的节点是叶子节点。
删除的节点有一个孩子。 1)左不空右为空;2)左为空右不空
删除的节点有两个孩子。
而第四种情况树最复杂的 - 左不为空右不为空。
一种方法是:选择右孩子继承,且将左子树移动到右孩子中最左侧节点处。
另一种方法是:找到删除节点中左子树中最右侧的叶子节点或者右子树中最左侧的叶子节点,然后将这个节点的值替换到删除节点中。然后直接删除这个叶子节点就可以了。
相对来说第二种方法在实现上面不改变树的高度,更优一些(实现上面稍微复杂一点,因为要多一次递归删除叶子节点)。
具体的代码实现:
public TreeNode deleteNode(TreeNode root, int key) {
//条件1
if(root == null) return root;
//如果找到了这个节点 - 判断节点孩子空的情况
if(root.val == key){
//都为空
if(root.left == null && root.right == null){
return null;
}
if(root.left != null && root.right == null){
return root.left;
}
if(root.left == null && root.right != null){
return root.right;
}
//左右孩子都不为空
if(root.left != null && root.right != null){
//查找继承者
TreeNode successor = root.right;
while(successor.left != null){
successor = successor.left;
}
//替换值
root.val = successor.val;
//删除successor节点
root.right = deleteNode(root.right, successor.val);
return root;
}
}
//重新构建树
root.left = deleteNode(root.left, key);
root.right = deleteNode(root.right, key);
return root;
}
参考资料:
https://programmercarl.com/0450.删除二叉搜索树中的节点.html#迭代法