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

目录

Leetcode - 235

Leetcode - 701

Leetcode - 450


Leetcode - 235

三种情况,若p,q的值同时小于root的值,那么p,q肯定存在于root的左子树里,且其公共祖先肯定也存在于左子树里,若p,q值同时大于root的值,那么p,q肯定存在于root的右子树里。若root的值存在于p,q之间,那么root肯定是p,q的最近祖先,为什么呢?因为此时,无论root是向左子树还是右子树继续遍历那么一定会漏掉p,q其中一个,所以root肯定为最近祖先

递归  有返回值,最近祖先

递归终止,root为None,返回root。

此题不涉及前中后序遍历,这是由于二叉搜索树的特性,第一种情况就递归左子树,第二种情况就递归右子树,第三种情况就直接返回

def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':

        def traversal(root,p,q):
            if not root:
                return root

            if root.val < p.val and root.val < q.val:
                right = traversal(root.right,p,q)
#其实这里,包括底下,都不用加判断,因为公共祖先是一定存在的,且肯定是在这个子树里
                if right:return right

            elif root.val > p.val and root.val > q.val:
                left = traversal(root.left,p,q)
                if left:return left

            else:
                return root

        return traversal(root,p,q)

不用递归也可以,写一个while循环,在root不为None的情况下,分为三种情况讨论,root值小就向右,否则向左,root在中间就直接返回

def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':

        while root:
            if root.val < p.val and root.val < q.val:
                root = root.right
            
            elif root.val > p.val and root.val > q.val:
                root = root.left

            else:
                return root

Leetcode - 701

这题的暴力法和递归我都是一口气写出来的,想清楚就好,这题关键就在于找叶子节点,然后插入,root值小就往右,否则往左,若左子树右子树为空,就直接加入节点。

def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
        if not root:
            return TreeNode(val)
#因为root最终要到叶子节点的位置,此处要像链表一样设置一个虚拟头结点,指向原来的根节点
        pre = TreeNode()
        pre.left = root
        while root:
            if root.val > val:
                if root.left == None:
                    root.left = TreeNode(val)
                    break
                root = root.left

            elif root.val < val:
                if root.right == None:
                    root.right = TreeNode(val)
                    break
                root = root.right

        return pre.left

递归法:

有返回值

终止条件:root为None, 此时说明到了根节点的下一个节点,此处返回要添加的节点即可,因为这里回溯到上一层就是叶子节点,返回这个节点作为叶子节点的孩子。

若root的值小,则递归右子树,根据终止条件,若此时返回值不为None,则说明root为叶子节点,root.right = 递归的返回值即可

若root值大,同理若左子树递归结果不为None,将递归结果作为root的左子节点

因为递归回返回到根节点,最后返回root即可。

def insert(root,val):
            if not root:
                return TreeNode(val)

            if root.val > val:
                left = insert(root.left,val)
                if left:
                    root.left = left

            if root.val < val:
                right = insert(root.right,val)
                if right:
                    root.right = right

            return root

        return insert(root,val)

Leetcode - 450

这题说实话,不听讲解我是肯定写不出来的hh

递归:  返回值为新树的根节点

终止条件,这里有大量的处理逻辑,以及可能会返回子树,这里其实和701很像,都是添加或删除节点,是对树做结构上的变化,在这种题目里终止条件会返回节点或者子树一类的,因为这里会作为回溯的结果返回给上一层节点,作为其左子树或者右子树

这里分五种情况讨论:1. root为None,即没有找到要删除的节点,直接返回None,2.找到了,但是该节点为叶子节点,这里也返回None,因为删除该叶子节点,只需要将这个节点的父节点指向空即可。3.找到了,左子树为空,右子树不为空,则将右子树作为改节点父节点的子树即可, 这里返回root的右子节点4.找到了,左子树不为空,右子树为空,同理,返回左子树。5.左右子树均不为空,这里最难的情况,这里将左右子树根节点返回都可,这里以返回右子树为例,明确一点,要删除的节点的值是处于左右子树中间的,与其最相邻的值是左树根节点和右树最左边的节点,即右树的最小节点,所以这里是找到右树最左节点,然后让root的左子树作为他的左子树即可,最后返回root.right。

接着是递归的过程,若root值小,则递归右子树,右子树的返回结果直接作为root的新的右子树

若root值更大,则将左子树递归的结果作为root的新的左子树。因为值不等的时候会一直递归,直到满足相等时,才会有相应的新树返回给上一层,这里的上一层就是要删除节点的父节点。所以这里直接用root.left或者root.right接收返回值是合理的,此时root就是父节点。

因为递归最后要回溯到根节点,所以最后返回root即可

def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
        def delete(root,key):
            if root == None:
                return root
            
            if root.val == key:
                if root.left == None and root.right == None:
                    return None
                
                elif root.left == None and root.right != None:
                    return root.right

                elif root.left !=  None and root.right == None:
                    return root.left

                else:
                    cur = root.right
                    #这里一直向左遍历,直到左子树为空,就找到了最左的叶节点
                    while cur.left:
                        cur = cur.left

                    cur.left = root.left
                    return root.right

            if root.val < key:
                root.right = delete(root.right,key)

            if root.val > key:
                root.left = delete(root.left,key)

            return root

        return delete(root,key)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值