Leetcode刷题笔记22:二叉树8(最近公共祖先与二叉搜索树的插入)

102 阅读4分钟

导语

leetcode刷题笔记记录,本篇博客记录二叉搜索树部分的题目,主要题目包括:

  • 236 二叉树的最近公共祖先
  • 235 二叉搜索树的最近公共祖先
  • 701 二叉搜索树中的插入操作

Leetcode 236. 二叉树的最近公共祖先

题目描述

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

示例 1:

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

解法

使用递归来解决这道题目,一共分为以下两种情况:

  1. 节点左子树中包含p(或者q),右子树中包含q(或者p),那么该节点就是p和q的最近公共祖先。
  2. 节点本身p(q),它拥有一个子孙节点q(p)

递归三部曲:

  1. 递归函数返回值以及参数:需要递归函数返回最近公共节点,如果遇到p或者q,就把q或者p返回,返回值不为空,就说明找到了q或者p。
  2. 终止条件:
    • 遇到空的话,因为树都是空了,所以返回空。
    • 如果 root == q,或者 root == p,说明找到 q p ,则将其返回,
  3. 单层递归逻辑中,本题函数有返回值,是因为回溯的过程需要递归函数的返回值做判断,但本题我们依然要遍历树的所有节点。之前已经在二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值? (opens new window)中讨论了递归函数有返回值就是要遍历某一条边,但有返回值也要看如何处理返回值!

如果递归函数有返回值,如何区分要搜索一条边,还是搜索整个树呢?

搜索一条边的写法:

if (递归函数(root->left)) return ;

if (递归函数(root->right)) return ;

搜索整个树写法:

left = 递归函数(root->left);  // 左
right = 递归函数(root->right); // 右
leftright的逻辑处理;         // 中 

区别在于:

在递归函数有返回值的情况下:如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,如果搜索整个树,直接用一个变量left、right接住返回值,这个left、right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也是回溯)

本题代码如下:

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if root is None:       # 返回值
            return None
        if root==p or root==q:
            return root
        left = self.lowestCommonAncestor(root.left, p, q)   # 左
        right = self.lowestCommonAncestor(root.right, p, q) # 右
        if left != None and right != None:                  # 中
            return root
        elif left is None and right is not None:
            return right
        elif left is not None and right is None:
            return left
        else:
            return None

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

递归法

本题是236的简单版本,即二叉树被约束为了二叉搜索树,因此可以利用这个良好的排序性质来简化代码:递归法的代码如下:

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if root.val < p.val and root.val < q.val:
            return self.lowestCommonAncestor(root.right, p, q)
        elif root.val > p.val and root.val > q.val:
            return self.lowestCommonAncestor(root.left, p, q)
        else:
            return root

迭代法

层次遍历法如下:

class Solution:
    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.二叉搜索树中的插入操作

题目描述

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

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

 

示例 1:

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

解法

只要按照二叉搜索树的规则去遍历,遇到空节点就插入节点就可以了。

递归法

class Solution:
    def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
        if root is None:
            return TreeNode(val)
        
        if val < root.val:
            root.left = self.insertIntoBST(root.left, val)
        else:
            root.right = self.insertIntoBST(root.right, val)

        return root

迭代法

层次遍历法需要双指针,pre指针用于记录当前指针cur变为None时的上一个节点。之后,新建一个节点挂到pre上即可。

class Solution:
    def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
        if root is None:
            return TreeNode(val)
        
        cur = root
        pre = None
        while cur:
            pre = cur
            if cur.val < val:
                cur = cur.right
            else:
                cur = cur.left
        if val < pre.val:
            pre.left = TreeNode(val)
        else:
            pre.right = TreeNode(val)

        return root