class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
if (A == null || B == null) return false;
if (A.val == B.val && check(A, B)) return true;
return isSubStructure(A.left, B) || isSubStructure(A.right, B);
}
private boolean check(TreeNode A, TreeNode B) {
if (B == null) return true;
if (A == null) return false;
return A.val == B.val && check(A.left, B.left) && check(A.right, B.right);
}
}
#首先判断B是否是以A为根节点的子树,如果是子树,那B一定是A的子结构,如果不是子树,递归判断B是否是A左右子树的子结构
class Solution:
def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
if A == None or B == None:
return False
if self.isSubTree(A,B):
return True
if self.isSubStructure(A.left,B) or self.isSubStructure(A.right,B):
return True
return False
# 判断Tb是否是以Ta为根结点的子树
def isSubTree(self,Ta,Tb):
if Tb == None:
return True
if Ta == None:
return False
if Ta.val != Tb.val:
return False
return self.isSubTree(Ta.left,Tb.left) and self.isSubTree(Ta.right,Tb.right)
func isSubStructure(A *TreeNode, B *TreeNode) bool {
if A == nil || B == nil{
return false
}
if Help(A, B){
return true
}
return isSubStructure(A.Left, B) || isSubStructure(A.Right, B)
}
func Help(A *TreeNode, B *TreeNode) bool {
// B空了
if B == nil{
return true
}
if A == nil || A.Val != B.Val {//A空了 或者A的值与B不相等了
return false
}
//A的左子树 B的左子树继续递归
isLeft := Help(A.Left, B.Left)
//A的右子树 B的右子树继续递归
isRight := Help(A.Right, B.Right)
return isLeft && isRight
}
时间复杂度 O(MN) : 其中 M,N 分别为树 A 和 树 B的节点数量;先序遍历树 A 占用 O(M) ,每次调用 recur(A, B) 判断占用 O(N) 。
空间复杂度 O(M) : 当树 A 和树 B 都退化为链表时,递归调用深度最大。当 M ≤N 时,遍历树 A 与递归判断的总递归深度为 M ;当 M>N 时,最差情况为遍历至树 A 叶子节点,此时总递归深度为 M。
class Solution {
public:
bool isSubStructure(TreeNode* A, TreeNode* B) {
if (A == NULL || B == NULL)
return false;
if (isSubTree(A, B))
return true;
if (isSubStructure(A->left, B) || isSubStructure(A->right, B))
return true;
return false;
}
// 判断A,B是否相等
bool isSubTree(TreeNode* A, TreeNode* B) {
if (B == NULL) // B 遍历完了没返回 false,证明都匹配上了
return true;
if (A == NULL) // A 遍历完了没返回,证明没匹配完,返回 false
return false;
if (A->val != B->val)
return false;
return (isSubTree(A->left, B->left) && isSubTree(A->right, B->right));
}
};
class Solution:
def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
def recur(A, B):
if not B:
return True
if not A or A.val != B.val:
return False
return recur(A.left, B.left) and recur(A.right, B.right)
return bool(A and B) and (recur(A, B) or self.isSubStructure(A.left, B) or self.isSubStructure(A.right, B))
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
if (A == null && B == null) {
return true;
}
if (A == null || B == null) {
return false;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(A);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
if (isSubStructureCore(node, B)) {
return true;
}
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
return false;
}
private boolean isSubStructureCore(TreeNode A, TreeNode B) {
Queue<TreeNode> queue = new LinkedList<>();
queue.add(A);
queue.add(B);
boolean res = true;
while (!queue.isEmpty()) {
TreeNode a = queue.poll();
TreeNode b = queue.poll();
if (a.val != b.val) {
res = (res && false);
}
if (b.left == null) {
res = (res && true);
} else {
if (a.left == null) {
res = (res && false);
} else {
queue.add(a.left);
queue.add(b.left);
}
}
if (b.right == null) {
res = (res && true);
} else {
if (a.right == null) {
res = (res && false);
} else {
queue.add(a.right);
queue.add(b.right);
}
}
}
return res;
}
}
递归法:时间复杂度O(MN),空间复杂度O(M + N)。
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
if (A == null || B == null) {
return false;
}
if (A == null && B == null) {
return true;
}
return isSubStructureCore(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);
}
private boolean isSubStructureCore(TreeNode A, TreeNode B) {
if (B == null) {
return true;
}
if (A == null) {
return false;
}
return A.val == B.val && isSubStructureCore(A.left, B.left) && isSubStructureCore(A.right, B.right);
}
}
class Solution:
def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
if not (A and B): return False
if A.val == B.val and self.find(A.left,B.left) and self.find(A.right,B.right):
return True
return self.isSubStructure(A.left,B) or self.isSubStructure(A.right,B)
<pre><code>def find(self,root1,root2):
if not root2:return True
if not root1:return False
if root1.val ==root2.val:
return self.find(root1.left,root2.left) and self.find(root1.right,root2.right)
else:return False
</code></pre>
class Solution:
def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
def recur(A, B):
if not B: return True
if not A or A.val != B.val: return False
return recur(A.left, B.left) and recur(A.right, B.right)
return bool(A and B) and (recur(A, B) or self.isSubStructure(A.left, B) or self.isSubStructure(A.right, B))
class Solution:
def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
def recur(A,B):
if not B:
return True
if not A or A.val != B.val:
return False
return recur(A.left, B.left) and recur(A.right,B.right)
return bool(A and B) and (recur(A,B) or self.isSubStructure(A.left,B) or self.isSubStructure(A.right, B))
共以下 48 个回答
注意判断相等用两个等号
O(N^2) DFS 递归判断·
● 复杂度: O(n*m)【n为树A的节点,m为树B的节点个数】先序遍历树 A 占用 O(n) ,每次调用 isSubTree判断占用 O(m)
● 空间复杂度: o(h)【h为树A的高度】
时间复杂度 O(MN) 空间复杂度树A的高度(递归的层数)
时间复杂度 O(MN) : 其中 M,N 分别为树 A 和 树 B的节点数量;先序遍历树 A 占用 O(M) ,每次调用 recur(A, B) 判断占用 O(N) 。
空间复杂度 O(M) : 当树 A 和树 B 都退化为链表时,递归调用深度最大。当 M ≤N 时,遍历树 A 与递归判断的总递归深度为 M ;当 M>N 时,最差情况为遍历至树 A 叶子节点,此时总递归深度为 M。
需要写两个递归函数
时间复杂度:O(N*M)
空间复杂度:O(H) H为树A的高度
感觉是两层递归,一层递归判断两树是否包含,一层递归遍历A树。
时间复杂度O(M*N),最差的情况是A的每个节点都和B的头节点相等,全部遍历
空间复杂度O(M+N)
时间复杂度O(N) 空间复杂度O(m+n) 两个二叉树的空间大小
时间复杂度 O(n)
空间复杂度:O(n)
时间复杂度:O(MN)
时间复杂度 O(MN):其中 M,NM,N 分别为树 A和 树 B 的节点数量;先序遍历树 A 占用 O(M) ,每次调用 isSubTree判断占用 O(N)。
空间复杂度 O(n)
思路:
先比头节点,根结点不同,递归找左右,结点找到相同头节点后;
对比树的结构是否相同,先比根结点是否相同,相同比左右子树是否相同
时间复杂度 O(MN):其中 M,NM,N 分别为树 AA 和 树 BB 的节点数量;先序遍历树 AA 占用 O(M)O(M) ,每次调用 recur(A, B) 判断占用 O(N)O(N)。
空间复杂度 O(n)。
思路:以树A中每一个节点为根节点形成一颗子树,看这个子树是否包含树B。
迭代法:时间复杂度O(MN),空间复杂度取决于队列中的节点个数。
递归法:时间复杂度O(MN),空间复杂度O(M + N)。
思路:
1、先序遍历树 A 中的每个节点(n_A),对应函数 isSubStructure(A, B)。
2、判断树 A 中 以节点n_A 为根节点的子树 是否包含树 B ,对应函数 recur(A, B)。
递归还是不会,感觉迭代好想一些 时间复杂度O(mn),空间复杂度O(n)
啊
思路:需要定义一个函数判断B是否是A节点为根节点的一个子树,做递归进行判断
空间复杂度O(n),时间复杂度O(mn)
解题思路:先遍历A的每一个节点nA,再判断B是否为nA的子树。
时间复杂度:O(MN),M表示A节点数量,N表示B节点数量
空间复杂度:O(M)
1、前序遍历,A中的每个节点均作为某子树的根与B子树比较。如果以某个节点为根的子节点与B相同,则返回true
2、A中节点数n,每次比较B子树节点m,时间O(mn),空间O(1)
//时间:O(MN). 空间O(N)
//递归
class Solution {
public:
bool issame(TreeNode* a,TreeNode* b){
if(bnullptr) return true;
if(anullptr || a->val!=b->val) return false;
return issame(a->left,b->left)&&issame(a->right,b->right);
}
};
时间:O(mn),空间:O(n)
双层的前序遍历
看了一下地哥讲解的思路,递归判断,直到B为空为true,如果不匹配,就判断A的left和right,直到A为null为false
时间O(n)
空间O(n)
注意只要A的左子树包含B或者A的右子树包含B,则满足条件,因此最后的条件应该是或
剑指 Offer 26. 树的子结构
这道题目是26、27、28 三道递归中相对较难的一道题。
27题只需要对单根节点进行递归即可,不需要辅助的递归函数。
28题需要构造一个辅助递归函数,因为原本的只有一个形参的函数并不能完成需要的递归任务,官方给出的is函数并不需要成为递归函数。
26题不止需要构造一个递归函数,同时is函数与构造出来的函数都要进行递归调用,类似于一个双重循环。
思路
类似于两层循环,外层遍历所有节点,内层从遍历到目前的节点开始判断是不是一致。
和字符串匹配的暴力搜索核心思想非常像。
is函数:做深搜操作
构造的递归函数 check(a,b)
检查b树是不是可以嵌入a树并以a节点为根节点(这句话有点绕,没想好怎么写的更明白
分为五种情况:a空b空,a不空b空,a空b不空,a不空b不空(包含aval与bval是否相等两种)
复杂度
时间 完全等同于字符串暴力匹配 O(M*N),M为A树节点树,N为B树节点树
空间 递归深度无论M>N或者M<\N,均为O(M),在左倾树或者右倾树(退化为链表)时达到
其他问题
最开始卡住的原因是,自己尝试模拟的时候,如果一个节点在check的时候,从check发生处下面很多层出现失配,不应该返回check下一层开始匹配,应该考虑不需要回溯那么多层。
其实就是类似于KMP算法一样不需要回溯,这也是我为什么反复说和字符串匹配非常像。
我认为这道题应该还存在更简单的解法,类似于KMP那样的,但是还没想明白。
代码
时间复杂度O(mn) 空间复杂度O(n)
时间复杂度O(nm),O(lgnlgm)
时间:O(mn),空间:O(n)
剑指 Offer 26. 树的子结构(10.28)
#