IT面试题---如何确定一棵二叉树是另一棵二叉树的子树(2)

不了解题目背景的同学请先阅读: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





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值