剑指 Offer 26. 树的子结构

 

思路#

方法一:如果B的先序序列是A的先序序列的子序列,并且B的中序序列也是A的中序序列的子序列,则B是A的子结构。这种方法比较暴力。

方法二:对A的每一个结点和B进行比较(这里可以使用先序遍历):

  如果A->val == B->val,则A的左子树和右子树也要和B对应的左子树右子树相同。

  如果A->val != B->val,则A的左子树或者右子树要和B相同。

注意:因为树A中可能有值相同的结点,所以必须对A的每一个结点都和B进行比较。比如如下测试样例:

[4,2,3,4,5,6,7,8,9]
[4,8,9]

方法二的代码实现#

复制代码
 1 /**
 2  * Definition for a binary tree node.
 3  * struct TreeNode {
 4  *     int val;
 5  *     TreeNode *left;
 6  *     TreeNode *right;
 7  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 8  * };
 9  */
10 class Solution {
11 public:
12     //先序遍历,判断A的每一个结点
13     bool isSubStructure(TreeNode* A, TreeNode* B) {
14         if(B == NULL || A == NULL)
15             return false;
16         if(check(A, B)) 
17             return true;
18         return isSubStructure(A->left, B) || isSubStructure(A->right, B);
19     }
20 
21     bool check(TreeNode* A, TreeNode* B) {
22         if(B == NULL) {
23             return true;
24         }
25 
26         if(A == NULL) {
27             return false;
28         }
29 
30         if(A->val == B->val) {
31             return check(A->left, B->left) && check(A->right, B->right);
32         } else {
33             return false;
34         }
35     }
36 
37 };
复制代码

 

如果A中不含值相同的结点时,可以不需要对A先序遍历每个节点进行判断。可以直接用如下代码判断:

复制代码
 1 /**
 2  * Definition for a binary tree node.
 3  * struct TreeNode {
 4  *     int val;
 5  *     TreeNode *left;
 6  *     TreeNode *right;
 7  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 8  * };
 9  */
10 class Solution {
11 public:
12     bool isSubStructure(TreeNode* A, TreeNode* B) {
13         if(B == NULL)
14             return false;
15         return check(A, B);
16     }
17 
18     //只有A中不含重复元素的时候,才能用此方法判断
19     bool check(TreeNode* A, TreeNode* B) {
20         if(B == NULL) {
21             return true;
22         }
23 
24         if(A == NULL) {
25             return false;
26         }
27 
28         if(A->val == B->val) {
29             return check(A->left, B->left) && check(A->right, B->right);
30         } else {
31             return check(A->left, B) || check(A->right, B);
32         }
33     }
34 };
复制代码

 

复杂度分析#

时间复杂度 O(MN) : 其中 M,N分别为树 A 和 树 B 的节点数量;先序遍历树 A 占用 O(M) ,每次调用 check(A, B) 判断占用 O(N) 。
空间复杂度 O(M) : 当树 A 和树 B 都退化为链表时,递归调用深度最大。当 M≤N 时,遍历树 A与递归判断的总递归深度为 M ;当 M>N时,最差情况为遍历至树 A 叶子节点,此时总递归深度为 M。

 

posted @   拾月凄辰  阅读(88)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
历史上的今天:
2019-10-25 使用内部Servlet转发JSP后页面的JS,CSS等资源引入问题的解决
点击右上角即可分享
微信分享提示
主题色彩