代码随想录算法训练营二十四期第二十二天|LeetCode235. 二叉搜索树的最近公共祖先、LeetCode701. 二叉搜索树中的插入操作、LeetCode450. 删除二叉搜索树中的节点

一、LeetCode235. 二叉搜索树的最近公共祖先

题目链接:235. 二叉搜索树的最近公共祖先
题目描述:

给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。

示例 1:

代码随想录算法训练营二十四期第二十二天|LeetCode235. 二叉搜索树的最近公共祖先、LeetCode701. 二叉搜索树中的插入操作、LeetCode450. 删除二叉搜索树中的节点_第1张图片

输入:root = [4,2,7,1,3], val = 5
输出:[4,2,7,1,3,5]
解释:另一个满足题目要求可以通过的树是:

代码随想录算法训练营二十四期第二十二天|LeetCode235. 二叉搜索树的最近公共祖先、LeetCode701. 二叉搜索树中的插入操作、LeetCode450. 删除二叉搜索树中的节点_第2张图片

示例 2:

输入:root = [40,20,60,10,30,50,70], val = 25
输出:[40,20,60,10,30,50,70,null,null,25]

示例 3:

输入:root = [4,2,7,1,3,null,null,null,null,null,null], val = 5
输出:[4,2,7,1,3,5]

提示:

  • 树中的节点数将在 [0, 104]的范围内。
  • -108 <= Node.val <= 108
  • 所有值 Node.val 是 独一无二 的。
  • -108 <= val <= 108
  • 保证 val 在原始BST中不存在。
算法分析:

利用递归。

如果当前节点为空,则不存在最近公共祖先,返回null.

如果当前节点的值同时大于pq的值,说明pq节点在当前节点的左边,向左递归继续寻找最近公共祖先,如果找到了就把最近公共祖先返回。

同理如果当前节点的值同时小于pq的值,说明pq节点都在当前节点的右边,向右递归继续寻找最近公共祖先,找到了,就把最近公共祖先返回。

如果当前节点的左右子树都没有找到最近公共祖先,说明当前节点就是pq的公共祖先,返回当前节点。

代码如下:

class Solution {
 
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return null;//如果当前节点为空,返回null

        if(root.val > p.val && root.val > q.val) {//如果当前节点的值同时大于pq的值,说明pq节点在当前节点的左子树
            TreeNode left = lowestCommonAncestor(root.left, p, q);//向左子树递归寻找最近公共祖先
            if(left != null) return left;//如果找到了就返回最近公共祖先
        }
        if(root.val < p.val && root.val < q.val) {//如果当前节点值同时小于pq,说明pq节点在当前节点的右子树
            TreeNode right = lowestCommonAncestor(root.right,p , q);//向右子树递归寻找最近公共祖先
            if(right != null) return right;//如果找到了就返回
        }
        return root;//如果左右子树都没有最近公共祖先,说明当前节点就是最近公共祖先,返回当前节点
    }
}

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

题目链接:701. 二叉搜索树中的插入操作
题目描述:

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉搜索树:  root = [6,2,8,0,4,7,9,null,null,3,5]

代码随想录算法训练营二十四期第二十二天|LeetCode235. 二叉搜索树的最近公共祖先、LeetCode701. 二叉搜索树中的插入操作、LeetCode450. 删除二叉搜索树中的节点_第3张图片

示例 1:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6。

示例 2:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。

说明:

  • 所有节点的值都是唯一的。
  • p、q 为不同节点且均存在于给定的二叉搜索树中。
算法分析

利用递归。

如果当前节点为空,将val值创建成节点返回。

如果当前节点的值大于val,通过递归将节点创建在当前节点的右子树。

如果当前节点的值小于val,通过递归将节点创建在当前节点的左子树。

代码如下:

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if(root ==  null) {//如果当前为空,将val创建成节点后返回
            TreeNode node = new TreeNode(val);
            return node;
        }
        if(root.val > val) root.left = insertIntoBST(root.left,val);//如果val小于当前节点的值,通过递归,将val插入到当前节点的左子树。
        else if(root.val < val) root.right = insertIntoBST(root.right,val);//如果val大于当前节点的值,通过递归将val插入到右子树

        return root;//最后返回当前节点
    }
}

三、LeetCode450. 删除二叉搜索树中的节点

题目链接:450. 删除二叉搜索树中的节点
题目描述:

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

  1. 首先找到需要删除的节点;
  2. 如果找到了,删除它。

示例 1:

代码随想录算法训练营二十四期第二十二天|LeetCode235. 二叉搜索树的最近公共祖先、LeetCode701. 二叉搜索树中的插入操作、LeetCode450. 删除二叉搜索树中的节点_第4张图片

输入:root = [5,3,6,2,4,null,7], key = 3
输出:[5,4,6,2,null,null,7]
解释:给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
另一个正确答案是 [5,2,6,null,4,null,7]。


代码随想录算法训练营二十四期第二十二天|LeetCode235. 二叉搜索树的最近公共祖先、LeetCode701. 二叉搜索树中的插入操作、LeetCode450. 删除二叉搜索树中的节点_第5张图片

示例 2:

输入: root = [5,3,6,2,4,null,7], key = 0
输出: [5,3,6,2,4,null,7]
解释: 二叉树不包含值为 0 的节点

示例 3:

输入: root = [], key = 0
输出: []

提示:

  • 节点数的范围 [0, 104].
  • -105 <= Node.val <= 105
  • 节点值唯一
  • root 是合法的二叉搜索树
  • -105 <= key <= 105

进阶: 要求算法时间复杂度为 O(h),h 为树的高度。

算法分析:

如果当前节点为空,说明没有要删除的节点,返回null。

如果当前节点值小于目标值key,说明要删除的节点在当前节点的右子树,向右递归去删除目标节点。

同理如果当前节点的值大于目标值key,说明要删除的节点在当前节点的左子树,向左递归去删除目标节点。

如果当前节点的值等于目标值key,也即当前节点为要删除的节点,那么对该节点进行删除操作:

1、如果当前节点只有右子树,那么返回当前节点的右子节点即可(相当于删除当前节点)

2、如果当前节点只有左子树,那么返回当前节点的左子节点。

3、如果当前节点既有左子树也有右子树,那么将左子树的根节点放到右子树的左下角位置(为了保持删除当前节点后,还具有二叉搜索树的性质,想一想这时为什么)

4、如果当前节点没有左右子树,那么直接返回null。

要注意回溯,每层递归时如果不删除当前节点都要将当前节点返回。

代码如下:

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if(root == null) return root;//如果当前节点为空,返回null
        if(root.val < key) root.right = deleteNode(root.right, key);//如果当前节点的值小于key说明目标节点可能在当前节点的右子树,向右递归去删除目标节点
        else if(root.val > key) root.left = deleteNode(root.left, key);//如果当前节点大于key说明目标节点可能在当前节点的左子树,向左递归去删除目标节点
        else {//当前节点的值等于key,即当前节点为目标节点,对当前节点进行删除操作
            if(root.left == null && root.right != null) return root.right;//如果目标节点只有右子树,返回右子树的根节点
            else if(root.left != null && root.right == null) return root.left;//如果目标节点只有左子树,返回左子树的根节点
            else if(root.left != null && root.right != null) {//如果目标节点有左右子树,将左子树的根节点,放到右子树的左下角(为了保证二叉搜索树性质)。
                TreeNode node = root.right;
                while(node.left != null) node = node.left;
                node.left = root.left;
                return root.right;
            }else {//如果左右子树都为空,返回null即可
                return null;
            }
        }
        return root;//注意回溯,返回当前节点
    }
}

总结

对于二叉树的操作,我们要有递归和回溯的思想。

你可能感兴趣的:(算法,数据结构)