题目
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两个节点,可能的关系有:
- p与q在同一层;
- 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。这其中的特殊情况为:
- root==None:此时无解
- root就是p或q:此时root即为解
- 在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。
图源
队列(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!')