代码随想录算法训练营第二十二天 | 235. 二叉搜索树的最近公共祖先,701. 二叉搜索树中的插入操作,450. 删除二叉搜索树中的节点

235. 二叉搜索树的最近公共祖先

相对于 二叉树的最近公共祖先 本题就简单一些了,因为 可以利用二叉搜索树的特性。 

题目链接/文章讲解:代码随想录

视频讲解:二叉搜索树找祖先就有点不一样了!| 235. 二叉搜索树的最近公共祖先_哔哩哔哩_bilibili

重点:

1. 二叉搜索树的最近公共祖先一定是第一个遇到的在p和q中间的值,不可能是第二个。因为如果遇到了第一个再接着向左遍历将错过在右边的值,往右遍历将错过在左边的值。

2. 二叉搜索树自带了搜索方向。根据值的大小比较可以判断出遍历方向

思路:

递归法:

1. 确定参数及返回值

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)

2. 确定终止条件

if (root == null) { return null; }

3. 确定单层递归的逻辑

if (root.val > p.val && root.val > q.val) {}
if (root.val < p.val && root.val < q.val) {}

迭代法:

1. 因为是二叉搜索树,根据值的大小我们就可以确定搜索方向,直接while循环即可

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // 终止条件
        if (root == null) {
            return null;
        }
        // 当前节点的值比p和q都大,往左走减小
        if (root.val > p.val && root.val > q.val) {
            TreeNode left = lowestCommonAncestor(root.left, p, q);
            if (left != null) {
                return left;
            }
        }
        // 当前节点的值比p和q都小,往右走增大
        if (root.val < p.val && root.val < q.val) {
            TreeNode right = lowestCommonAncestor(root.right, p, q);
            if (right != null) {
                return right;
            }
        }
        // 当前节点的值值刚刚好在中间的情况
        return root;
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // 终止条件
        if (root == null) {
            return null;
        }
        while (root != null) {
            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 {
                // 当前节点的值正好在中间,说明找到了公共最近祖先
                return root;
            }
        }
        return null;
}

701. 二叉搜索树中的插入操作

本题比想象中的简单,大家可以先自己想一想应该怎么做,然后看视频讲解,就发现 本题为什么比较简单了。

题目链接/文章讲解:代码随想录

视频讲解:原来这么简单? | LeetCode:701.二叉搜索树中的插入操作_哔哩哔哩_bilibili

重点:

1. 二叉搜索树根据值就能判断出左右方向

2. 遇到叶子节点了就说明找到位置了

思路:

递归法:

1. 确定参数及返回值:

不用遍历全部树,只要找到对应位置即可,所以需要返回值

public TreeNode insertIntoBST(TreeNode root, int val)

2. 确定终止条件

遇到叶子节点说明找到位置了

if (root == null) { return new TreeNode(val); }

3. 确定单层递归的逻辑

当前节点的值小了,就往右走。当前节点的值大了,就往左走

if (root.val > val) {
            TreeNode left = insertIntoBST(root.left, val);
            root.left = left;
}
if (root.val < val) {
            TreeNode right = insertIntoBST(root.right, val);
            root.right = right;
}

迭代法:

1. 双指针。用一个parent指针来保存上一个遍历的节点,这样方便后面找到位置后赋值。

public TreeNode insertIntoBST(TreeNode root, int val) {
        // 终止条件
        // 遇到叶子节点,找到位置了
        if (root == null) {
            return new TreeNode(val);
        }
        // 当前节点的值大了,往左走
        if (root.val > val) {
            TreeNode left = insertIntoBST(root.left, val);
            // 回溯回来的时候正好就可以赋值给当前节点
            root.left = left;
        }
        // 当前节点的值小了,往右走
        if (root.val < val) {
            TreeNode right = insertIntoBST(root.right, val);
            // 回溯回来的时候正好就可以赋值给当前节点
            root.right = right;
        }
        return root;
}
public TreeNode insertIntoBST(TreeNode root, int val) {
        // 终止条件
        // 遇到叶子节点,找到位置了
        if (root == null) {
            return new TreeNode(val);
        }
        TreeNode cur = root;
        // parent记录上一个节点
        TreeNode parent = null;
        while (cur != null) {
            parent = cur;
            // 根据大小选择方向
            if (cur.val > val) {
                cur = cur.left;
            } else {
                cur = cur.right;
            }
        }
        TreeNode node = new TreeNode(val);
        // parent就是保存好的对应的父节点
        if (val < parent.val) {
            parent.left = node;
        } else {
            parent.right = node;
        }
        return root;
}

450.  删除二叉搜索树中的节点

相对于 插入操作,本题就有难度了,涉及到改树的结构

题目链接/文章讲解:代码随想录

视频讲解:调整二叉树的结构最难!| LeetCode:450.删除二叉搜索树中的节点_哔哩哔哩_bilibili

重点:

1. 总共五种情况,没找到删除节点,删除节点为叶子节点,删除节点左不为空右为空,删除节点左为空右不为空,删除节点左右都不为空

思路:

递归法:

1. 确定参数及返回值

public TreeNode deleteNode(TreeNode root, int key)

2. 确定终止条件

没找到删除节点和找到了删除节点

删除节点分五种情况,分别讨论即可,注意左右都不为空的情况,需要先把要删除节点的左子树衔接给右子树的最小值当作左子树,然后再把要删除节点的右子树回溯给上一层递归

if (root == null) { return null; }
// 找到要删除的节点
if (root.val == key) {
    // 叶子节点,直接return null就可以删除了
    if (root.left == null && root.right == null) { return null; }
    // 左不为空,右为空,把左返回回溯给上一层递归赋值
    else if (root.left != null && root.right == null) { return root.left; }
    // 左为空,右不为空,把右返回回溯给上一层递归赋值
    else if (root.left == null && root.right != null) { return root.right; }
    // 左右都不为空    
    else {
        // 把右子树中的最小值衔接作要删除节点的左子树的根节点
        TreeNode rightSmallest = root.right;
        while (rightSmallest.left != null) { rightSmallest = rightSmallest.left; }
        rightSmallest.left = root.left;
        // 衔接好root的左子树后,返回原来root的右子树即可
        return root.right;
    }
}

3. 确定单层递归的逻辑

if (root.val > key) { root.left = deleteNode(root.left, key); }
if (root.val < key) { root.right = deleteNode(root.right, key); }

迭代法:

1. 双指针,一个指针为要删除的节点,一个指针为要删除节点的上一个节点,然后分五种情况讨论

public TreeNode deleteNode(TreeNode root, int key) {
        // 终止条件
        if (root == null) {
            return null;
        }
        // 找到要删除的节点
        if (root.val == key) {
            if (root.left == null && root.right == null) {
                // 叶子节点,直接return null就可以删除了
                return null;
            } else if (root.left != null && root.right == null) {
                // 左不为空,右为空,把左返回回溯给上一层递归赋值
                return root.left;
            } else if (root.left == null && root.right != null) {
                // 左为空,右不为空,把右返回回溯给上一层递归赋值
                return root.right;
            } else {
                // 左右都不为空
                // 把右子树中的最小值衔接作要删除节点的左子树的根节点
                TreeNode rightSmallest = root.right;
                while (rightSmallest.left != null) {
                    rightSmallest = rightSmallest.left;
                }
                rightSmallest.left = root.left;
                // 衔接好root的左子树后,返回原来root的右子树即可
                return root.right;
            }
        }
        if (root.val > key) {
            root.left = deleteNode(root.left, key);
        }
        if (root.val < key) {
            root.right = deleteNode(root.right, key);
        }
        return root;
}
public TreeNode deleteNode(TreeNode root, int key) {
        if (root == null) {
            return null;
        }
        // 找到要删除的,并且记录他的上一个节点
        TreeNode cur = root;
        TreeNode pre = null;
        while (cur != null) {
            if (cur.val == key) {
                break;
            }
            pre = cur;
            if (cur.val > key) {
                cur = cur.left;
            } else {
                cur = cur.right;
            }
        }
        // 如果是头节点要删除的话
        if (pre == null) {
            return deleteOneNode(cur);
        }
        // 要删的是左边的
        if (pre.left != null && pre.left.val == key) {
            pre.left = deleteOneNode(cur);
        }
        // 要删的是右边的
        if (pre.right != null && pre.right.val == key) {
            pre.right = deleteOneNode(cur);
        }
        return root;
}

private TreeNode deleteOneNode(TreeNode target) {
        if (target == null) {
            return null;
        }
        // 叶子节点
        if (target.left == null && target.right == null) {
            return null;
        }
        // 左不为空,右为空
        if (target.left != null && target.right == null) {
            return target.left;
        }
        // 左为空,右不为空
        if (target.left == null && target.right != null) {
            return target.right;
        }
        // 左右都不为空
        // 把要删除节点的左子树衔接好
        TreeNode cur = target.right;
        while (cur.left != null) {
            cur = cur.left;
        }
        cur.left = target.left;
        return target.right;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值