1. 判断【树B】是否是【树A】的【子结构】/【子树】
1.1 概念阐述
首先阐述一个概念:子结构匹配与子树匹配
子树匹配:
子树匹配就是A的子结构与B完全匹配
子结构匹配:
子结构匹配就是A的子结构与B部分匹配,A的子结构允许有与B不匹配的其他结构
如何判断是【子树匹配】还是【子结构】呢?我觉得最重要的还是读题。
并且这两个匹配算法在代码上相差比较小,如果用某种方法A不了我们可以想想是不是思考错题意了
1.2 子结构匹配的情况
建议看这个题解:
面试题26. 树的子结构(先序遍历 + 包含判断,清晰图解)
例如:
给定的树 A:
3
/ \
4 5
/ \
1 2
给定的树 B:
4
/
1
recur
判定函数的记忆方法:
- 如果B为空,说明走到底了,True
- 如果A为空,说明没救了,False
- 判断元素是否相等
- 继续往下递归(and条件)
递归里面有递归,很难想到
def recur(A, B):
if B is None:
return True
if A is None:
return False
if not A.val==B.val:
return False
return recur(A.left, B.left) and recur(A.right, B.right) # and
class Solution:
def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
# bool
return bool(A and B) and ( recur(A, B) or self.isSubStructure(A.left, B) or self.isSubStructure(A.right, B) )
1.3 子树匹配的情况
这题如果是子结构匹配的话,示例2是可以通过的。我们做题的第一件事就是理解题意。
这题其实可以复用上题的代码,就是需要修改一下recur
函数的判断条件
【子结构匹配】的recur函数:
def recur(A, B):
if B is None: # 只要到了B的边界(None值)就视为完成了匹配
return True
if A is None:
return False
if not A.val==B.val:
return False
return recur(A.left, B.left) and recur(A.right, B.right) # and
【子树匹配】的recur函数:
def recur(A, B):
if A is None and B is None: # B到了边界时,A必须也到边界
return True
if bool(A) ^ bool(B): # 如果AB其中之一为None,另外一个不为None,就表明匹配失败
return False
if not A.val==B.val:
return False
return recur(A.left, B.left) and recur(A.right, B.right)
相同点:
AB都不为None,值(val)不匹配,返回False
不同点:(加粗)
A | B | 子树匹配 | 子结构匹配 |
---|---|---|---|
None | not None | False | False |
None | None | True | True |
not None | None | False | True |
子树匹配完整代码:
def recur(A, B):
if A is None and B is None:
return True
if bool(A) ^ bool(B):
return False
if not A.val==B.val:
return False
return recur(A.left, B.left) and recur(A.right, B.right)
class Solution:
def isSubtree(self, A: TreeNode, B: TreeNode) -> bool:
return bool(A and B) and ( recur(A, B) or self.isSubtree(A.left, B) or self.isSubtree(A.right, B) )
2. 查找所有重复子树
东哥笔记:
本质上是数的后序遍历,使用了序列化的技巧
class Solution:
NULL = "#"
def findDuplicateSubtrees(self, root: TreeNode) -> List[TreeNode]:
memo = collections.defaultdict(int)
res = []
def traverse(node: TreeNode):
if node is None:
return self.NULL
seq = ",".join([traverse(node.left), traverse(node.right), str(node.val)])
if memo[seq] == 1: # 巧妙地去重
res.append(node)
memo[seq] += 1
return seq
traverse(root)
return res