中级农民
- 积分
- 126
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2015-4-22
- 最后登录
- 1970-1-1
|
本帖最后由 zhuli19901106 于 2015-7-25 21:26 编辑
Subtree
题意:给定两棵二叉树T1和T2,判断T2是否为T1的子树。此处子树的定义是,可以找到某个T1的节点,如果以这节点为根的话,就和T2长得一模一样。
解法1:暴力递归解决,效率并不高。感觉这题很像字符串匹配里的text=”aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa“,pattern=”aaaab“这种情况。如果总是在结尾才发现匹配失败的话,效率会变得很低。所以这题的数据肯定是很宽容的,否则不可能AC。如果能像AC自动机那样计算出每个节点的回溯位置,是不是可以提高效率呢?
代码1:- /**
- * Definition of TreeNode:
- * class TreeNode {
- * public:
- * int val;
- * TreeNode *left, *right;
- * TreeNode(int val) {
- * this->val = val;
- * this->left = this->right = NULL;
- * }
- * }
- */
- class Solution {
- public:
- /**
- * @param T1, T2: The roots of binary tree.
- * @return: True if T2 is a subtree of T1, or false.
- */
- bool isSubtree(TreeNode *T1, TreeNode *T2) {
- if (sameTree(T1, T2)) {
- return true;
- }
- if (T1 == NULL) {
- return false;
- }
- return isSubtree(T1->left, T2) || isSubtree(T1->right, T2);
- }
- private:
- bool sameTree(TreeNode *r1, TreeNode *r2) {
- if (r1 == NULL) {
- if (r2 == NULL) {
- return true;
- }
- return false;
- }
- if (r2 == NULL) {
- return false;
- }
- if (r1->val != r2->val) {
- return false;
- }
- return sameTree(r1->left, r2->left) && sameTree(r1->right, r2->right);
- }
- };
复制代码 复杂度1:时间O(N1 * N2),空间一样。
解法2:依然是暴力搜索,但加上判断高度的条件。如果两棵树高度不一样,肯定不可能相同。没想到运行时间比第一种还慢,不解。
代码2:- #include <unordered_map>
- using namespace std;
- /**
- * Definition of TreeNode:
- * class TreeNode {
- * public:
- * int val;
- * TreeNode *left, *right;
- * TreeNode(int val) {
- * this->val = val;
- * this->left = this->right = NULL;
- * }
- * }
- */
- class Solution {
- public:
- /**
- * @param T1, T2: The roots of binary tree.
- * @return: True if T2 is a subtree of T1, or false.
- */
- bool isSubtree(TreeNode *T1, TreeNode *T2) {
- height.clear();
- height[NULL] = 0;
- calcHeight(T1);
- calcHeight(T2);
- return subtree(T1, T2);
- }
- private:
- unordered_map<TreeNode *, int> height;
-
- bool subtree(TreeNode *T1, TreeNode *T2) {
- if (sameTree(T1, T2)) {
- return true;
- }
- if (T1 == NULL) {
- return false;
- }
- return subtree(T1->left, T2) || subtree(T1->right, T2);
- }
-
- void calcHeight(TreeNode *root) {
- if (root == NULL) {
- return;
- }
- calcHeight(root->left);
- calcHeight(root->right);
- height[root] = max(height[root->left], height[root->right]) + 1;
- }
-
- bool sameTree(TreeNode *r1, TreeNode *r2) {
- if (height[r1] != height[r2]) {
- // No need to go further
- return false;
- }
- if (r1 == NULL) {
- if (r2 == NULL) {
- return true;
- }
- return false;
- }
- if (r2 == NULL) {
- return false;
- }
- if (r1->val != r2->val) {
- return false;
- }
- return sameTree(r1->left, r2->left) && sameTree(r1->right, r2->right);
- }
- };
复制代码 复杂度2:时间O(N1 * N2),空间一样。
解法3:刚才在解法1中提到了AC自动机里那种建立回溯指针的思路,实际写了之后,发现比较难,搞不定。于是我又从KMP的角度去入手。想出了这么个思路:如果T1的前序和中序遍历中都包含了T2的前序和中序遍历,那么T2就是T1的子树。此处的遍历序列还要把空指针也表示进去,否则就会出现二义性。比如前序{1,1,1},中序{1,1,1},这样你无法确定这棵树长什么样,如果变成{1,#,1,#,1,#,#}和{#,1#,1,#,1,#},就没有二义性了。得到两棵树的前序、中序序列之后,按照T1作为文本,T2作为模式,进行KMP匹配。就可以在线性时间求出结果了。
当然,这么做写起来是很麻烦的,我的KMP代码都是copy自己以前写的。
代码3:- // This is my idea :)
- #include <climits>
- #include <vector>
- using std::vector;
- /**
- * Definition of TreeNode:
- * class TreeNode {
- * public:
- * int val;
- * TreeNode *left, *right;
- * TreeNode(int val) {
- * this->val = val;
- * this->left = this->right = NULL;
- * }
- * }
- */
- typedef long long int LL;
- class Solution {
- public:
- /**
- * @param T1, T2: The roots of binary tree.
- * @return: True if T2 is a subtree of T1, or false.
- */
- bool isSubtree(TreeNode *T1, TreeNode *T2) {
- if (T1 == NULL) {
- return T2 == NULL;
- }
- if (T2 == NULL) {
- return true;
- }
- pre1.clear();
- pre2.clear();
- in1.clear();
- in2.clear();
-
- preorder(T1, pre1);
- preorder(T2, pre2);
- inorder(T1, in1);
- inorder(T2, in2);
-
- return KMPMatch(pre1, pre2) && KMPMatch(in1, in2);
- }
- private:
- vector<LL> pre1, in1, pre2, in2;
- vector<int> next;
- int ls, lt;
-
- void preorder(TreeNode *r, vector<LL> &v) {
- if (r == NULL) {
- v.push_back(LONG_LONG_MAX);
- return;
- }
- v.push_back(r->val);
- preorder(r->left, v);
- preorder(r->right, v);
- }
-
- void inorder(TreeNode *r, vector<LL> &v) {
- if (r == NULL) {
- v.push_back(LONG_LONG_MAX);
- return;
- }
- inorder(r->left, v);
- v.push_back(r->val);
- inorder(r->right, v);
- }
-
- void getNext(vector<LL> &t) {
- int i, j;
- i = 0;
- j = -1;
-
- next.clear();
- next.resize(lt + 1);
- next[0] = -1;
- while (i < lt) {
- if (j == -1 || t[i] == t[j]) {
- ++i;
- ++j;
- next[i] = j;
- } else {
- j = next[j];
- }
- }
- }
-
- bool KMPMatch(vector<LL> &s, vector<LL> &t) {
- ls = s.size();
- lt = t.size();
- getNext(t);
-
- int i, j;
- i = j = 0;
- while (i < ls) {
- if (j == -1 || s[i] == t[j]) {
- ++i;
- ++j;
- } else {
- j = next[j];
- }
- if (j == lt) {
- return true;
- }
- }
- return false;
- }
- };
复制代码 复杂度3:时间O(N1 + N2),空间一样。
|
|