剑指 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。