二叉树匹配类问题
判断二叉树的子树是否包含另一个二叉树
这类匹配类题目分为两步
- 先将根节点匹配;
- 根节点匹配后,对子树进行匹配。
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
例如:
给定的树 A:
3
/ \
4 5
/ \
1 2
给定的树 B:
4
/
1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。
示例 1:
输入:A = [1,2,3], B = [3,1]
输出:false
示例 2:
输入:A = [3,4,5,1,2], B = [4,1]
输出:true
题解
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
return (A!=null&&B!=null)&&(recur(A,B)||isSubStructure(A.left,B)||isSubStructure(A.right,B));
}
private boolean recur(TreeNode A,TreeNode B){
if(B==null){
return true;
}
if(A==null||A.val!=B.val){
return false;
}
return recur(A.left,B.left)&&recur(A.right,B.right);
}
}
代码中主要涉及 主函数和 dfs 函数 两个部分。与以上思路对应,主函数对应根节点的匹配,dfs 函数对应匹配其他节点。
dfs 函数
dfs 函数将注意力集中在了根节点已经匹配的情况。当从根节点同时开始向下遍历时,我们进行以下判断:
如果 A 和 B 同时遍历到了 null,说明匹配成功,返回 True
如果 A 或 B 提前遍历到了 null,一棵树匹配完了,另一棵却没有,说明子树的结构是不同的,则匹配失败,返回 False
主函数
在将视野放远来看,主函数则解决了如何确定 A 的哪个节点是 B 的根节点。
如果 A 的当前节点值与 B 的根节点值相同,我们调用 dfs 函数判断子树是否也相同;如果不同,我们就递归调用主函数来寻找 A 的哪个节点与 B 的根节点匹配。
复杂度
L-101 对称二叉树
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
题解
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null)
return true;
//去匹配树的左右子树是否是镜像匹配
return recur(root.left,root.right);
}
boolean recur(TreeNode A,TreeNode B){
//如果都为空,说明匹配完成
if(A==null&&B==null)
return true;
//若有一个节点为空,说明匹配未完成,则说明两棵树不匹配
if((A==null&&B!=null)||(A!=null&&B==null))
return false;
//若节点值不相等,这说明两棵树不匹配
if(A.val!=B.val)
return false;
//继续去递归匹配树的镜像
return recur(A.left,B.right)&&recur(A.right,B.left);
}
}
复杂度
- 时间复杂度:O(N)
- 空间复杂度:与递归使用的栈的深度有关,这里递归的深度不超过N,所以渐进的空间复杂度为O(N)
L-1367
给你一棵以 root 为根的二叉树和一个 head 为第一个节点的链表。
如果在二叉树中,存在一条一直向下的路径,且每个点的数值恰好一一对应以 head 为首的链表中每个节点的值,那么请你返回 True ,否则返回 False 。
一直向下的路径的意思是:从树中某个节点开始,一直连续向下的路径。
题解
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSubPath(ListNode head, TreeNode root) {
//如果链表为空,则一定能匹配成功
if(head==null)
return true;
//如果树为空,链表不空,则匹配失败
if(root==null)
return false;
//寻找与head相同的节点,通过根节点的左子树与右子树进行不断地匹配
return recur(head,root)||isSubPath(head,root.left)||isSubPath(head,root.right);
}
public boolean recur(ListNode A,TreeNode B){
if(A==null)
return true;
if(B==null||B.val!=A.val)
return false;
//匹配链表与树的左子树或者匹配链表与树的右子树是否匹配,返回结果
return recur(A.next,B.left)||recur(A.next,B.right);
}
}
L-572 另一个树的字树
给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。
题解
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
if(t==null) return true;
if(s==null) return false;
return recur(s,t)||isSubtree(s.left,t)||isSubtree(s.right,t);
}
public boolean recur(TreeNode A,TreeNode B){
if(A==null&&B==null){
return true;
}
if((A==null&&B!=null)||(A!=null&&B==null))
return false;
if(A.val!=B.val)
return false;
return recur(A.left,B.left)&&recur(A.right,B.right);
}
}
老生常谈了