572.另一棵树的子树
今天在看面经的时候看到一个题,大意是:判断链表有没有在二叉树中出现过。想起好像力扣有一道类似的,就想都总结下。
分析
对于树类型的题,要注意从根节点分析(每个节点都是它那颗子树的根节点)。
要判断是否是子树,即是判断结构和值是否相同。那么我们需要从结构和值方面考虑:
- 如果
root
和subRoot
都为null
,满足 - 如果
root
和subRoot
其中一个为null
,另一个不为null
,不满足 - 如果
root
和subRoot
都不为空,若值相等则该节点满足,继续判断左右的节点;否则,从root
的左右子树中继续找
我们使用isSubtree(root, subRoot)
来判断subRoot
是否是root
为根的树的子树;使用dfs(root, subRoot)
来判断root
为根的子树和subRoot
为根的子树是否一致(结构和值相同)。
对于isSubtree(root, subRoot)
函数,返回true
的条件是:
dfs(root, subRoot)
返回true
,如上图中root = 4
时,即以当前节点为根的子树和subRoot
一致- 或者,
root
的左子树存在和subRoot
一致的子树 - 或者,
root
的右子树存在和subRoot
一致的子树
对于dfs(root, subRoot)
函数,返回true
的条件是:
-
root
为根的子树和subRoot
为根的子树结构一致(可以理解为有相同的分叉数) -
root
和subRoot
的值相等,并且左子树和右子树中的值也分别相等
最坏情况下,对于每一个root
上的点,都需要做一次深度优先搜索来和subRoot
匹配,时间复杂度为O(nm)
,n
是root
的节点个数,m
是subRoot
的节点个数。(想象两个只有一点差别的树,树中节点值都为1
)
依照上面的分析,具体代码如下:
class Solution {
public:
bool isSubtree(TreeNode* root, TreeNode* subRoot) {
if(!root && !subRoot)
return true;
if(!root || !subRoot)
return false;
return dfs(root, subRoot) || isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}
bool dfs(TreeNode* root, TreeNode* subRoot) {
if(!root && !subRoot)
return true;
if(!root || !subRoot)
return false;
return root->val == subRoot->val && dfs(root->left, subRoot->left) && dfs(root->right, subRoot->right);
}
};
判断子链
即判断二叉树root
是否包含链表subRoot
。由于没地方做测试,这里用树模拟链表,树的左指针
模拟链表的next指针
,例如链表[4,2,0]
用树表示为:
与上面的二叉树类似,isSubtree()
函数不用更改,需要更改的是dfs()
函数部分。因为只要左右子树有一个和链表一致即可,且链表只有一个方向(left
),所以核心代码更改如下:
bool dfs(TreeNode* root, TreeNode* subRoot) {
...
return root->val == subRoot->val && (dfs(root->left, subRoot->left) || dfs(root->right, subRoot->left));
}
在力扣上的测试结果如下(代码测试不完全,但是主要是体现思路),修改dfs
函数逻辑后,返回true
代表root
中存在于subRoot
一致的子链: