235. 二叉搜索树的最近公共祖先
1. 当root的val在p与q之间时,root为最近公共祖先
反证法:如果不是最近,向左/右向下遍历,都会错过p,q中的某一个
图源:代码随想录
2. 因为BST已经有序,所以此题不用遍历整颗树
如果有返回值,区分遍历边/整颗树:
图源同上
返回值不为空的时候立即返回,不需要后续的逻辑处理,所以只是搜索一条边
3. 代码习惯:
不好判断p,q大小,可以先判断两种其他情况(root<p&&root<q和root>p&&root>q),之后剩下的情况就是root的val在p和q之间
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null)return root;
if(root.val>p.val&&root.val>q.val){
TreeNode left = lowestCommonAncestor(root.left,p,q);
if(left!=null)return left;
}else if(root.val<p.val&&root.val<q.val){
TreeNode right = lowestCommonAncestor(root.right,p,q);
if(right!=null)return right;
}
return root;
}
}
//简化
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);
if (root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q);
return root;
}
}
//迭代
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
while (true) {
if (root.val > p.val && root.val > q.val) {
root = root.left;
} else if (root.val < p.val && root.val < q.val) {
root = root.right;
} else {
break;
}
}
return root;
}
}
701.二叉搜索树中的插入操作
二叉搜索树插入,因为BST已经有序,所以肯定不用遍历整颗树,只需要遍历边即可
返回值返回节点,可以顺便把新加入节点连上
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null) // 如果当前节点为空,也就意味着val找到了合适的位置,此时创建节点直接返回。
return new TreeNode(val);
if (root.val < val){
root.right = insertIntoBST(root.right, val); // 递归创建右子树
}else if (root.val > val){
root.left = insertIntoBST(root.left, val); // 递归创建左子树
}
return root;
}
}
迭代法,需要双指针来记录父节点(parent),
需要单独讨论空节点
需要记录根节点,作为返回值
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null) return new TreeNode(val);
TreeNode newRoot = root;
TreeNode pre = root;
while (root != null) {
pre = root;
if (root.val > val) {
root = root.left;
} else if (root.val < val) {
root = root.right;
}
}
if (pre.val > val) {
pre.left = new TreeNode(val);
} else {
pre.right = new TreeNode(val);
}
return newRoot;
}
}
450.删除二叉搜索树中的节点
通过递归的效果来实现删除(通过返回值)
五种情况:
这里就把二叉搜索树中删除节点遇到的情况都搞清楚。
有以下五种情况:
- 第一种情况:没找到删除的节点,遍历到空节点直接返回了
- 找到删除的节点
- 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
- 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
- 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
- 第五种情况:1. 左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。2. 找到左孩子中的最大值,或者右孩子中的最小值,递归删除该最近(左子树最大/右子树最小)节点(此被删除节点肯定为2-4情况),并把其重新放到删除节点处
删除二叉树中的节点:
1. 因为没有有序性,需要遍历整颗二叉树
2. 代码中目标节点(要删除的节点)被操作了两次:
- 第一次是和目标节点的右子树最左面节点交换(一直交换到最深处最左面节点)。
- 第二次直接被NULL覆盖了。
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) return root;
if (root.val == key) {
if (root.right == null) { // 这里第二次操作目标值:最终删除的作用
return root.left;
}
TreeNode cur = root.right;
while (cur.left!=null) {
cur = cur.left;
}
swap(root.val, cur.val); // 这里第一次操作目标值:交换目标值其右子树最左面节点。
}
root.left = deleteNode(root.left, key);
root.right = deleteNode(root.right, key);
return root;
}
};