【Python Leetcode】236. Lowest Common Ancestor of a Binary Tree

题目

Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”

给定一个二叉树,找到指定两节点的最近共同祖先。
根据维基百科, 最近共同祖先(LCA) 是指树 T上最底层的满足p与q均为其后代节点的节点(节点a属于a的后代节点)。

如下图,节点5与1的LCA为3,节点5与3的LCA为3.
例:一棵树
图源Leetcode题目示例

格式

Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
Output: 3
Explanation: The LCA of nodes 5 and 1 is 3.
注意此处虽然展示时给出的是list,但实际上传递给Solution的input是TreeNode类型。

给出代码

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

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        

思路1:Index与Node关系(在最坏情况下超时)

如果把full binary tree按中序遍历(In-Order Traverse)放入list:

当root index为0时:
index为a的node,其父节点为:a/2 (a为奇数) 或a/2-1(a为偶数),
子节点为:2a+1 与2a+2.

因此,如果能将所有node的值写入list,则可以推出每一个给定节点的父节点。对p和q两个节点,可能的关系有:

  1. p与q在同一层;
  2. p与q不在同一层。
    因此,对于p, q, p的父节点pp, q的父节点qp,需做以下比较:
if pp == qp:
	# 两者父节点相同,LCA为pp(即qp)  
elif pp == q:
    # p的父节点是q, LCA为q
elif p == qp:
    # q的父节点是p, LCA为p
else:
	# 均不满足,上溯后继续寻找
    return self.LCA(qp, pp)

代码实现如下:

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

treeList = [-1]
tem_q = collections.deque()

class Solution(object):
    def putP(self, p):
        if p != None:
            treeList.append(p.val)
            tem_q.appendleft(p.left)
            tem_q.appendleft(p.right)
        else:
            treeList.append(None)
            # tem_q.appendleft(None)
            # tem_q.appendleft(None)
            
    def getList(self, p):
        self.putP(p)
        while tem_q:
            self.putP(tem_q.pop())

    
    def findParent(self, a):
        # if a % 2 == 1:
        #     return a/2
        # else:
        #     return a/2-1
        return a/2
        
        
        
    def LCA(self, p, q):
        pp = self.findParent(p)
        qp = self.findParent(q)
        
        if pp == qp:
            return treeList[pp]    
        elif pp == q:
            return treeList[qp]
        elif p == qp:
                return treeList[pp]
        elif pp<=qp:
            return self.LCA(pp, qp)
        else:
            return self.LCA(qp, pp)
        
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        self.getList(root)
        # print(treeList)
        pidx = treeList.index(p.val)
        qidx = treeList.index(q.val)
        if pidx<qidx:
            return TreeNode(self.LCA(pidx,qidx))
        else:
            return TreeNode(self.LCA(qidx,pidx))

上面的方法容易超时,因此又实验了接下来的思路:
先把p与q所有的先辈节点按从远根端到近根端的顺序排列,再逐一比较返回最先重合的先辈节点。代码如下:

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


class Solution(object):
    def __init__(self):
        self.treeList = [None]
        self.tem_q = collections.deque()
        self.findP=False
        self.findQ=False
    
    def putP(self, p, pval, qval):

        if p != None:
            self.treeList.append(p.val)
            self.tem_q.appendleft(p.left)
            self.tem_q.appendleft(p.right)
            
            if p.val == pval:
                self.findP = True
            if p.val == qval:
                self.findQ = True
        else:
            self.treeList.append(None)
            if not (self.findP and self.findQ):
                self.tem_q.appendleft(None)
                self.tem_q.appendleft(None)
            
    def getList(self, p, pval, qval):
        self.putP(p, pval, qval)
        while self.tem_q:
            self.putP(self.tem_q.pop(), pval, qval)
        
    def LCA(self, p, q):
        while p != q:
            if p>q:
                p /= 2
            else:
                q /= 2
                
        return self.treeList[p]
        
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        
        self.getList(root, p.val, q.val)
        # print(self.treeList)
        pidx = self.treeList.index(p.val)
        qidx = self.treeList.index(q.val)
        if pidx<qidx:
            return TreeNode(self.LCA(pidx,qidx))
        else:
            return TreeNode(self.LCA(qidx,pidx))

然而这种情况在最差的环境下仍然会超时(即每一层只有最左与最右有节点,其它均空时)。

思路2:Recursion与二分法

由于上面的方法行不通,我们转换思路。试想,从root开始,如果p与q分别在root左右两子树,则root必为p与q的LCA。反之,如果p和q在root的同一侧,则表明存在更近的解。因此可将pq所在的那一子树当作新树,继续用相同的办法寻找LCA。这其中的特殊情况为:

  1. root==None:此时无解
  2. root就是p或q:此时root即为解
  3. 在recursion时要注意左子树或右子树为空的情况,防止null pointer报错。

代码实现如下:

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

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        if root == None:
            return None
        
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        
        if (left==None and right!=None):
            if root == p or root == q:
                return root
            else:
                return right
            
        elif (left!=None and right==None):
            if root == p or root == q:
                return root
            else:
                return left
            
        elif (left!=None and right!= None):
            return root
        
        elif (root==p or root==q):
            return root

知识背景

树(Tree)

Tree是不含有cycle的所有node都连接在一起的graph。Binary tree是指每个node最多有两个子节点的tree。Full binary tree / proper binary tree/ 2-tree指除叶节点外每个节点都有两个子节点的binary tree。 Complete tree指(不计最后一层)每一层都是满的,且所有节点都尽可能地先填满左侧的binary tree。
Full Binary TreeComplete Binary Tree
图源

队列(Queue)

实现FIFO的数据结构(以下是python中queue的用法,参考自)。Leetcode的Python环境不支持import queue,可使用deque代替。

import queue

q = queue.Queue()

# check empty
q.empty()
# pump in
q.put(1)
# pump out
q.get()

Python Deque

Deque集成了queue和stack(FILO)。以下是deque作为queue使用的代码示例(参考自):

from collections import deque

queue = collections.deque()
# pump in
queue.appendleft()
# pump out
queue.pop()

 # check empty
if not queue:
	print('Queue is empty!')

关于Deque Empty Check

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值