代码随想录训练营 Day20打卡 二叉树 part07 235. 二叉搜索树的最近公共祖先 701. 二叉搜索树中的插入操作 450. 删除二叉搜索树中的节点

代码随想录训练营 Day20打卡 二叉树 part07

一、 力扣235. 二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
在这里插入图片描述
示例 :
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。

版本一 递归法

此方法使用递归遍历来确定LCA,通过比较当前节点的值与 𝑝和 𝑞 的值来决定递归的方向。

class Solution:
    def traversal(self, cur, p, q):
        # 如果当前节点为空,则返回None
        if cur is None:
            return cur
        
        # 如果当前节点的值大于p和q的值,那么LCA位于当前节点的左子树
        if cur.val > p.val and cur.val > q.val:
            left = self.traversal(cur.left, p, q)
            if left is not None:
                return left
        
        # 如果当前节点的值小于p和q的值,那么LCA位于当前节点的右子树
        if cur.val < p.val and cur.val < q.val:
            right = self.traversal(cur.right, p, q)
            if right is not None:
                return right
        
        # 如果当前节点的值在p和q的值之间,或等于p或q的任一个,当前节点即为LCA
        return cur

    def lowestCommonAncestor(self, root, p, q):
        return self.traversal(root, p, q)

版本二 递归法 精简

这个版本是对第一个版本的递归法的精简,去除了一些冗余的检查和变量,直接在一个递归函数中完成所有逻辑。

class Solution:
    def lowestCommonAncestor(self, root, p, q):
        # 如果当前节点的值大于p和q的值,递归左子树
        if root.val > p.val and root.val > q.val:
            return self.lowestCommonAncestor(root.left, p, q)
        
        # 如果当前节点的值小于p和q的值,递归右子树
        elif root.val < p.val and root.val < q.val:
            return self.lowestCommonAncestor(root.right, p, q)
        
        # 否则,当前节点就是LCA
        else:
            return root

版本三 迭代法

此方法使用迭代而非递归来查找LCA,利用了相同的比较逻辑但避免了递归可能带来的栈溢出问题。

class Solution:
    def lowestCommonAncestor(self, root, p, q):
        # 循环遍历树,直到找到分叉点
        while root:
            # 如果当前节点的值大于p和q的值,向左移动
            if root.val > p.val and root.val > q.val:
                root = root.left
            
            # 如果当前节点的值小于p和q的值,向右移动
            elif root.val < p.val and root.val < q.val:
                root = root.right
            
            # 否则,当前节点就是LCA
            else:
                return root
        
        # 如果树被遍历完还没有找到LCA,返回None
        return None

力扣题目链接
题目文章讲解
题目视频讲解

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

给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果
示例 :
在这里插入图片描述
输入:root = [4,2,7,1,3], val = 5
输出:[4,2,7,1,3,5]

只要按照二叉搜索树的规则去遍历,遇到空节点就插入节点就可以了。例如插入元素10 ,需要找到末尾节点插入便可,一样的道理来插入元素15,插入元素0,插入元素6,需要调整二叉树的结构么? 并不需要。
在这里插入图片描述

版本一 递归法 1

实现思路:

  1. 创建 parent 属性来跟踪当前节点的父节点。
  2. 使用递归方法 traversal 进行遍历,当遍历到空节点时插入新的节点。
  3. 在遍历过程中,更新 parent 到当前节点。
  4. 在 insertIntoBST 方法中初始化根节点并开始遍历。
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def __init__(self):
        self.parent = None  # 初始化父节点为 None

    def traversal(self, cur, val):
        # 如果当前节点为空,表示找到了插入位置
        if cur is None:
            node = TreeNode(val)  # 创建一个新节点
            # 根据值的大小确定新节点是左孩子还是右孩子
            if val > self.parent.val:
                self.parent.right = node
            else:
                self.parent.left = node
            return

        # 更新父节点为当前节点
        self.parent = cur
        # 如果值小于当前节点值,递归遍历左子树
        if cur.val > val:
            self.traversal(cur.left, val)
        # 如果值大于当前节点值,递归遍历右子树
        if cur.val < val:
            self.traversal(cur.right, val)

    def insertIntoBST(self, root, val):
        self.parent = TreeNode(0)  # 初始化一个虚拟父节点
        # 如果根节点为空,创建一个新根节点
        if root is None:
            return TreeNode(val)
        # 从根节点开始递归遍历
        self.traversal(root, val)
        # 返回修改后的根节点
        return root

版本二 递归法 2

实现思路

  1. 如果根节点为空,直接返回一个新创建的节点。
  2. 使用 while 循环遍历树找到插入点。
  3. 在找到的插入点上插入新节点。
  4. 返回修改后的树的根节点。
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def insertIntoBST(self, root, val):
        # 如果根节点为空,创建一个新根节点
        if root is None:
            return TreeNode(val)
        
        parent = None  # 初始化父节点
        cur = root  # 从根节点开始遍历
        # 遍历树以找到插入位置
        while cur:
            parent = cur  # 更新父节点为当前节点
            if val < cur.val:
                cur = cur.left  # 如果值小于当前节点值,遍历左子树
            else:
                cur = cur.right  # 如果值大于当前节点值,遍历右子树
        
        # 在找到的插入位置插入新节点
        if val < parent.val:
            parent.left = TreeNode(val)
        else:
            parent.right = TreeNode(val)
        
        # 返回修改后的根节点
        return root

版本三 递归法3

实现思路

  1. 基本情况:如果根节点为空,创建一个新节点并返回。
  2. 使用递归方法在左子树或右子树中插入节点。
  3. 如果左子树或右子树为空,则直接在该位置创建新节点。
  4. 返回修改后的树的根节点。
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
        # 基本情况:如果根节点为空,创建一个新节点并返回
        if root is None:
            return TreeNode(val)
        
        # 递归插入到左子树或右子树
        if root.val > val:
            if root.left is None:
                root.left = TreeNode(val)  # 如果左子树为空,插入新节点
            else:
                self.insertIntoBST(root.left, val)  # 否则递归插入左子树
        elif root.val < val:
            if root.right is None:
                root.right = TreeNode(val)  # 如果右子树为空,插入新节点
            else:
                self.insertIntoBST(root.right, val)  # 否则递归插入右子树
        
        # 返回修改后的根节点
        return root

力扣题目链接
题目文章讲解
题目视频讲解

三、 力扣450. 删除二叉搜索树中的节点

本题需要把二叉搜索树中删除节点遇到的情况都搞清楚。

有以下五种情况:

第一种情况:没找到删除的节点,遍历到空节点直接返回了
找到删除的节点
第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。

第五种情况有点难以理解,看下面动画:在这里插入图片描述

版本一 递归法

实现思路

  1. 递归找到需要删除的节点。

  2. 根据删除节点的孩子情况处理删除操作:

    叶子节点直接删除。
    左孩子为空时,右孩子补位。
    右孩子为空时,左孩子补位。
    左右孩子都不为空时,将左子树放到右子树的最左节点上。

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def deleteNode(self, root, key):
        if root is None:
            return root  # 如果根节点为空,直接返回

        if root.val == key:  # 找到要删除的节点
            # 如果是叶子节点,直接删除
            if root.left is None and root.right is None:
                return None
            # 如果左孩子为空,右孩子补位
            elif root.left is None:
                return root.right
            # 如果右孩子为空,左孩子补位
            elif root.right is None:
                return root.left
            else:
                # 左右孩子都不为空的情况
                cur = root.right
                while cur.left is not None:  # 找到右子树中的最左节点
                    cur = cur.left
                cur.left = root.left  # 将删除节点的左子树放到右子树的最左节点的左孩子位置上
                return root.right  # 返回删除节点的右孩子作为新的根节点

        if root.val > key:
            root.left = self.deleteNode(root.left, key)  # 在左子树中递归删除目标节点
        if root.val < key:
            root.right = self.deleteNode(root.right, key)  # 在右子树中递归删除目标节点
        return root  # 返回根节点

版本二 递归法

实现思路

  1. 递归找到需要删除的节点。
  2. 如果右子树为空,直接返回左子树作为新的根节点。
  3. 如果右子树不为空,找到右子树中的最左节点,与要删除的节点值交换。
  4. 递归删除交换值后的节点。
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def deleteNode(self, root, key):
        if root is None:  # 如果根节点为空,直接返回
            return root

        if root.val == key:  # 找到要删除的节点
            if root.right is None:  # 如果右子树为空,直接返回左子树作为新的根节点
                return root.left
            cur = root.right
            while cur.left:  # 找到右子树中的最左节点
                cur = cur.left
            root.val, cur.val = cur.val, root.val  # 将要删除的节点值与最左节点值交换
        root.left = self.deleteNode(root.left, key)  # 在左子树中递归删除目标节点
        root.right = self.deleteNode(root.right, key)  # 在右子树中递归删除目标节点
        return root  # 返回根节点

力扣题目链接
题目文章讲解
题目视频讲解

  • 17
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值