不了解题目背景的同学请先阅读:IT面试题---如何确定一棵二叉树是另一棵二叉树的子树(1)
举例:下图中S显然是T的子树
Tree S x / \ a b \ c Tree T z / \ x e / \ \ a b k \ c
在之前的blog中我们讨论过了一种O(NM)的解法,这里我们讨论一种更为优化的解法,
我们注意到对于二叉树来说中序遍历和前序遍历可以唯一地识别一棵二叉树
因此算法描述如下:
1. 存储树T的中序遍历和前序遍历于数组inT, preT
2. 存储树S的中序遍历和前序遍历于数组inS, preS
3. 如果inS是inT的子序列且preS是preT的子序列,那么S就是T的子树
T的中序和前序遍历序列如下: inT[] = {a, c, x, b, z, e, k} preT[] = {z, x, a, c, b, e, k} S的中序和前序遍历序列如下: inS[] = {a, c, x, b} preS[] = {x, a, c, b} 显然inS是inT的子序列,preS是preT的子序列
但是上述算法仍然拥有缺陷,考虑以下两棵树,光从序列考察,S是T的子树,但很显然事实上它不是
Tree S x / \ a b / c PreS = xacb InS = caxb
Tree T x / \ a b / \ c d
PreT = xacbd InT = caxbd
原因在于以上序列没有将空指针考虑在内,因此如果我们做一个变化,将空孩子用$表示,则上述序列则变化为:
Tree S x / \ a b / c PreS = xac$$$b$$ InS = $c$a$x$b$
Tree T x / \ a b / \ c d
PreT = xac$$$b$d$$ InT = $c$a$x$b$d$
那么PreS自然就不是PreT的子序列了,这样我们的结论就成立了。
你还不放心?OK,让我们用这种方法再看看之前的例子
Tree S x / \ a b \ c PreS = xa$c$$b$$ inS = $a$c$x$b$ Tree T z / \ x e / \ \ a b k \ c
PreT = zxa$c$$b$$e$k$$ inT = $a$c$x$b$z$e$k$
显然,结论成立。
实现代码如下:
#include <iostream>
#include <cstring>
using namespace std;
#define MAX 100
// 二叉树节点
struct Node
{
char key;
struct Node *left, *right;
};
// 新建子节点
Node *newNode(char item)
{
Node *temp = new Node;
temp->key = item;
temp->left = temp->right = NULL;
return temp;
}
// 构造中序序列
void storeInorder(Node *root, char arr[], int &i)
{
if (root == NULL)
{
arr[i++] = '$';
return;
}
storeInorder(root->left, arr, i);
arr[i++] = root->key;
storeInorder(root->right, arr, i);
}
// 构造前序序列
void storePreOrder(Node *root, char arr[], int &i)
{
if (root == NULL)
{
arr[i++] = '$';
return;
}
arr[i++] = root->key;
storePreOrder(root->left, arr, i);
storePreOrder(root->right, arr, i);
}
/* S是不是T的子树 */
bool isSubtree(Node *T, Node *S)
{
if (S == NULL) return true;
if (T == NULL) return false;
// 打造S和T的中序序列
int m = 0, n = 0;
char inT[MAX], inS[MAX];
storeInorder(T, inT, m);
storeInorder(S, inS, n);
inT[m] = '\0', inS[n] = '\0';
// inS是不是inT的子序列
if (strstr(inT, inS) == NULL)
return false;
// 打造S和T的前序序列
m = 0, n = 0;
char preT[MAX], preS[MAX];
storePreOrder(T, preT, m);
storePreOrder(S, preS, n);
preT[m] = '\0', preS[n] = '\0';
// preS是不是preT的子序列
return (strstr(preT, preS) != NULL);
}
// 测试用例
int main()
{
Node *T = newNode('a');
T->left = newNode('b');
T->right = newNode('d');
T->left->left = newNode('c');
T->right->right = newNode('e');
Node *S = newNode('a');
S->left = newNode('b');
S->left->left = newNode('c');
S->right = newNode('d');
if (isSubtree(T, S))
cout << "Yes: S is a subtree of T";
else
cout << "No: S is NOT a subtree of T";
return 0;
}
显然该算法达到了线性时间复杂度
关于strstr的实现,可以参照
http://blog.csdn.net/v_july_v/article/details/7041827