剑指offer68题: 二叉树最近公共祖先

一:二叉搜索树的最近公共祖先

1、问题描述:
在一个长度为n的数组里的所有数字都在0到n-1的范围内。      数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。      例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
在这里插入图片描述
2、数据结构:
二叉搜索树
3、题解:
方法1:递归
二叉搜索树有个性质:即左子树的节点小于根节点,右子树的节点大于根节点,左右子树也是二叉搜索树。
最近公共祖先的定义: 设节点 root为节点 p,q 的某公共祖先,若其左子节点 root.left 和右子节点 root.right 都不是.p,q 的公共祖先,则称 root是 “最近的公共祖先” 。
最近的公共节点只能是下面几种情况:
1)p、q为root的子树,且分别在root的不同侧(即分别为左、右子树)
2)p = root,q在root的左(或右)子树
3)q = root,p在root的左(或右)子树
那么用递归去解题就是
若都大于root,则在右子树查找
若都小于root,则在左子树查找
否则返回为根节点。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

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)
        if root.val > p.val and root.val > q.val:
            return self.lowestCommonAncestor(root.left, p, q)
        return root

方法2:迭代
思路和递归差不多

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        #非递归
        node = root
        while node:
            if p.val > node.val and q.val > node.val:
                node = node.right
            elif p.val < node.val and q.val < node.val:
                node = node.left
            else:
                return node

优化:可以减少判断次数,先对p,q进行判断

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if p.val > q.val:#保证p <= q,减少判断次数
            p,q = q,p
        node = root
        while node:
            if p.val > node.val :
                node = node.right
            elif q.val < node.val:
                node = node.left
            else:
                return node

4、复杂度分析:

方法1:
时间复杂度:O(logN)
空间复杂度:O(logN)
方法2:
时间复杂度:O(logN)
空间复杂度:O(1)

二、二叉树的最近公共祖先

1、问题描述:
在这里插入图片描述
在这里插入图片描述
2、数据结构:

二叉树

3、题解:
方法1:
在这里插入图片描述
如果有指向父节点的指针,那么可以返回找到p或q的路径的反方向,问题转化为求两个链表第一个公共节点的问题。举例:如上图,加入,p、q分别为7、4,那么有7-2-5-3,4-2-5-3,两个链表的第一个公共节点是2,找到。
可以使用数组存储路径,找到第一个不相等的节点的前一个节点就是最近公共祖先。那么使用先序遍历。如上图,加入p、q分别为7,4,则有3-5-2-7,3-5-2-4,2为两个数组中第一个不相等的值的前一个值。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        def dfs(root,node,stack):
            if not root:
                return False
            stack.append(root)
            if root.val == node.val:
                return True
            if (dfs(root.left,node,stack) or dfs(root.right,node,stack)):
                return True
            stack.pop()#都没找到,向上回溯
        stack1 = []
        stack2 = []
        dfs(root,p,stack1)
        dfs(root,q,stack2)
        i ,res= 0,0
        while i < len(stack1) and i < len(stack2) and stack1[i] == stack2[i]:
            res = stack1[i]
            i += 1
        return res

方法2:
后序遍历,分析见第一题。
递归结束条件是:root为空或者等于p的值,或者等于q的值返回root.
因为二叉树没有二叉搜索树那样的性质,所以要先遍历左右子树,然后判断是否在左右子树中:
当左右子树为空时,返回;
当不在左子树中,返回右子树;
当不在右子树中,返回左子树;
当在两侧,说明找到最近公共祖先,返回即可。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if not root or p.val == root.val or q.val == root.val:
            return root
        left = self.lowestCommonAncestor(root.left,p,q)
        right = self.lowestCommonAncestor(root.right,p,q)
        if not left and not right:
            return 
        if not left:
            return right
        if not right:
            return left
        return root

4、复杂度分析:
两种方法都是:
时间复杂度:O(N)
空间复杂度:O(N)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值