/*
树的子结构:
输入两颗二叉树A和B,判断B是不是A的子结构。
例如:
树A: 树B
8 8
8 7 9 2
9 2
4 7
过程总结:
在树A中查找与树B中根节点一样的值,然后递归比较两者的左子树和右子树是否相同
递归出口是:树B的左右子树为空
这道题目有两个递归:
主递归:用于确定树A中的那个根节点与树B的根节点进行比较,一旦相同,调用子递归
子递归:用于判断两个树中是否具有相同的子结构。这里注意:如果小树先递归到空,说明是有子结构的。
如果两个根节点(前根遍历)都不同或者大树已经为空,肯定不是。如果根节点相同,那么分别遍历:
大树与小树的左子树 与 大树与小数的右子树 是否都具有子结构
输入:
输入可能包含多个测试样例,输入以EOF结束。
对于每个测试案例,输入的第一行一个整数n,m(1<=n<=1000,1<=m<=1000):n代表将要输入的二叉树A的节点个数(节点从1开始计数),m代表将要输入的二叉树B的节点个数(节点从1开始计数)。接下来一行有n个数,每个数代表A树中第i个元素的数值,接下来有n行,第一个数Ki代表第i个节点的子孩子个数,接下来有Ki个树,代表节点i子孩子节点标号。接下来m+1行,与树A描述相同。
输出:
对应每个测试案例,
若B是A的子树输出”YES”(不包含引号)。否则,输出“NO”(不包含引号)。
样例输入:
7 3
8 8 7 9 2 4 7
2 2 3
2 4 5
0
0
2 6 7
0
0
8 9 2
2 2 3
0
0
1 1
2
0
3
0
样例输出:
YES
NO
*/
/*
关键:
1 if(pSmallHead2 == NULL)//易错,这里小树为空必须放在前面,否则会出现大树也为空,但大树放在前面返回为假的情况
{
return true;
}
if(pBigHead1 == NULL)//递归出口
2 return judgeSubTree(pBigHead1->_left,pSmallHead2->_left) && judgeSubTree(pBigHead1->_right,pSmallHead2->_right);//两个根节点值相等,就返回左右子树的判定情况
3 if(pBigHead1->_iVal == pSmallHead2->_iVal)//如果头结点对应的值相同,则进行递归
{
bRes = judgeSubTree(pBigHead1,pSmallHead2);//递归主体
if(!bRes)
{
bRes = isSubTree(pBigHead1->_left,pSmallHead2);//递归调用左子树
}
if(!bRes)//这里,如果左子树如果找到,后面右子树就不需要查找了,因此需要加判定条件
*/
#include <stdio.h>
#include <string.h>
const int MAXSIZE = 1001;//1000个节点
typedef struct TreeNode
{
int _iVal;
struct TreeNode* _left;
struct TreeNode* _right;
}TreeNode;
TreeNode nodeArr1[MAXSIZE];
TreeNode nodeArr2[MAXSIZE];
int _iIndex1;
int _iIndex2;
TreeNode* createNode1()
{
++_iIndex1;
nodeArr1[_iIndex1]._left = nodeArr1[_iIndex1]._right = NULL;
return &nodeArr1[_iIndex1];
}
TreeNode* createNode2()
{
++_iIndex2;
nodeArr2[_iIndex2]._left = nodeArr2[_iIndex2]._right = NULL;
return &nodeArr2[_iIndex2];
}
//建大树
void buildBigTree(TreeNode** pHead,int* pArr,int n,TreeNode* (*pFun)())//如果刚开始就穿入的是空指针
{
if(pHead == NULL || *pHead == NULL || pArr == NULL || n <= 0)
{
return;
}
for(int i = 0 ; i < n ; i++)//先把各个单独的节点建立起来,其中首节点不需要建立,因为已经建立了
{
if(i)//建立其他节点
{
TreeNode* pNode = pFun();
pNode->_iVal = pArr[i];
//printf("%d ",pNode->_iVal);
}
else//对于头结点赋值
{
(*pHead)->_iVal = pArr[i];
}
}
//接下来构建二叉树的指向问题,共有n行,代表n个节点
for(int j = 1 ; j <= n; j++)
{
int iNum;//第1个节点表示,该节点有几个子节点
scanf("%d",&iNum);
int iLeft,iRight;
if(iNum == 0)//如果没有子节点,那么该节点无需设置,直接遍历下一个节点
{
continue;
}
else if(iNum == 1)//如果只有一个节点,默认为子节点编号左节点
{
scanf("%d",&iLeft);
nodeArr1[j]._left = &nodeArr1[iLeft];
}
else if(iNum == 2)//构建该节点的左右子节点
{
scanf("%d %d",&iLeft,&iRight);//接收子节点编号
nodeArr1[j]._left = &nodeArr1[iLeft];
nodeArr1[j]._right = &nodeArr1[iRight];
}
}
}
//建小树
void buildSmallTree(TreeNode** pHead,int* pArr,int n,TreeNode* (*pFun)())
{
if(pHead == NULL || *pHead == NULL || pArr == NULL || n <= 0)
{
return;
}
for(int i = 0 ; i < n ; i++)//先把各个单独的节点建立起来,其中首节点不需要建立,因为已经建立了
{
if(i)//建立其他节点
{
TreeNode* pNode = pFun();
pNode->_iVal = pArr[i];
}
else//对于头结点赋值
{
(*pHead)->_iVal = pArr[i];
}
}
//接下来构建二叉树的指向问题,共有n行,代表n个节点
for(int j = 1 ; j <= n; j++)
{
int iNum;//第1个节点表示,该节点有几个子节点
scanf("%d",&iNum);
int iLeft,iRight;
if(iNum == 0)//如果没有子节点,那么该节点无需设置,直接遍历下一个节点
{
continue;
}
else if(iNum == 1)//如果只有一个节点,默认为子节点编号左节点
{
scanf("%d",&iLeft);
nodeArr2[j]._left = &nodeArr2[iLeft];
}
else if(iNum == 2)//构建该节点的左右子节点
{
scanf("%d %d",&iLeft,&iRight);//接收子节点编号
nodeArr2[j]._left = &nodeArr2[iLeft];
nodeArr2[j]._right = &nodeArr2[iRight];
}
}
}
//主递归函数:设置头结点,对于头结点相同比较,一旦发现两者从头结点向下遍历。
//判断是否具有相同子结构的次递归函数:若发现头结点的值不同,则立即返回。否则返回左子树的比较情况和右子树
//的比较情况。一旦小树变空,说明是正确的。大数变空则是错误的。(递归出口)
//因为这里是遍历,因此不需要用二级指针。只有当需要改变的时候采用二级指针
bool judgeSubTree1(TreeNode* pBigHead1,TreeNode* pSmallHead2)
{
if(pSmallHead2 == NULL)//易错,这里小树为空必须放在前面,否则会出现大树也为空,但大树放在前面返回为假的情况
{
return true;
}
if(pBigHead1 == NULL)//递归出口
{
return false;
}
if(pBigHead1->_iVal == pSmallHead2->_iVal)//两个根节点值相等,就返回左右子树的判定情况
{
return judgeSubTree1(pBigHead1->_left,pSmallHead2->_left) &&
judgeSubTree1(pBigHead1->_right,pSmallHead2->_right);
}
else
{
return false;
}
}
bool judgeSubTree(TreeNode* pBigHead1,TreeNode* pSmallHead2)
{
if(pSmallHead2 == NULL)//易错,这里小树为空必须放在前面,否则会出现大树也为空,但大树放在前面返回为假的情况
{
return true;
}
if(pBigHead1 == NULL)//递归出口
{
return false;
}
if(pBigHead1->_iVal != pSmallHead2->_iVal)
{
return false;
}
return judgeSubTree(pBigHead1->_left,pSmallHead2->_left) && judgeSubTree(pBigHead1->_right,pSmallHead2->_right);//两个根节点值相等,就返回左右子树的判定情况
}
bool isSubTree1(TreeNode* pBigHead1,TreeNode* pSmallHead2)
{
if(!pBigHead1 || !pSmallHead2)//设置递归出口
{
return false;
}
bool bRes = false;//默认为假
if(pBigHead1->_iVal == pSmallHead2->_iVal)//如果头结点对应的值相同,则进行递归
{
bRes = judgeSubTree1(pBigHead1,pSmallHead2);//递归主体
if(!bRes)
{
bRes = isSubTree1(pBigHead1->_left,pSmallHead2);//递归调用左子树
}
if(!bRes)//这里,如果左子树如果找到,后面右子树就不需要查找了,因此需要加判定条件
{
bRes = isSubTree1(pBigHead1->_right,pSmallHead2);//递归调用右子树
}
}
return bRes;
}
bool isSubTree(TreeNode* pBigHead1,TreeNode* pSmallHead2)
{
bool bRes = false;//默认为假
if(pBigHead1 && pSmallHead2)//设置递归出口
{
if(pBigHead1->_iVal == pSmallHead2->_iVal)//如果头结点对应的值相同,则进行递归
{
bRes = judgeSubTree(pBigHead1,pSmallHead2);//递归主体
if(!bRes)
{
bRes = isSubTree(pBigHead1->_left,pSmallHead2);//递归调用左子树
}
if(!bRes)//这里,如果左子树如果找到,后面右子树就不需要查找了,因此需要加判定条件
{
bRes = isSubTree(pBigHead1->_right,pSmallHead2);//递归调用右子树
}
}
}
return bRes;
}
void process()
{
int n,m;
while(EOF != scanf("%d %d",&n,&m))
{
if(n <= 0 || n > 1000 || m <= 0 || m > 1000 || n < m)
{
printf("NO\n");
continue;
}
//建大树
int iArr1[MAXSIZE];
_iIndex1 = 0;//注意结点编号从1开始,注意记录头结点
memset(nodeArr1,NULL,sizeof(nodeArr1));
TreeNode* head1 = createNode1();//不能穿入空指针
TreeNode** pHead1 = &head1;
for(int i = 0 ; i < n ; i++)//接受树A的n各节点的值,这里应该顺便将各个节点创建出来
{
scanf("%d",&iArr1[i]);
}
buildBigTree(pHead1,iArr1,n,createNode1);
//建小树
int iArr2[MAXSIZE];
_iIndex2 = 0;//注意结点编号从1开始,注意记录头结点
memset(nodeArr2,NULL,sizeof(nodeArr2));
TreeNode* head2 = createNode2();//不能穿入空指针
TreeNode** pHead2 = &head2;
for(int j = 0 ; j < m ; j++)//接受树A的n各节点的值,这里应该顺便将各个节点创建出来
{
scanf("%d",&iArr2[j]);
}
buildSmallTree(pHead2,iArr2,m,createNode2);
//对已经建立的树,判断大树是否包含小树的子结构
bool bRes = isSubTree(head1,head2);
if(bRes)
{
printf("YES\n");
}
else
{
printf("NO\n");
}
}
}
int main(int argc,char* argv[])
{
process();
getchar();
return 0;
}
树的子结构:
输入两颗二叉树A和B,判断B是不是A的子结构。
例如:
树A: 树B
8 8
8 7 9 2
9 2
4 7
过程总结:
在树A中查找与树B中根节点一样的值,然后递归比较两者的左子树和右子树是否相同
递归出口是:树B的左右子树为空
这道题目有两个递归:
主递归:用于确定树A中的那个根节点与树B的根节点进行比较,一旦相同,调用子递归
子递归:用于判断两个树中是否具有相同的子结构。这里注意:如果小树先递归到空,说明是有子结构的。
如果两个根节点(前根遍历)都不同或者大树已经为空,肯定不是。如果根节点相同,那么分别遍历:
大树与小树的左子树 与 大树与小数的右子树 是否都具有子结构
输入:
输入可能包含多个测试样例,输入以EOF结束。
对于每个测试案例,输入的第一行一个整数n,m(1<=n<=1000,1<=m<=1000):n代表将要输入的二叉树A的节点个数(节点从1开始计数),m代表将要输入的二叉树B的节点个数(节点从1开始计数)。接下来一行有n个数,每个数代表A树中第i个元素的数值,接下来有n行,第一个数Ki代表第i个节点的子孩子个数,接下来有Ki个树,代表节点i子孩子节点标号。接下来m+1行,与树A描述相同。
输出:
对应每个测试案例,
若B是A的子树输出”YES”(不包含引号)。否则,输出“NO”(不包含引号)。
样例输入:
7 3
8 8 7 9 2 4 7
2 2 3
2 4 5
0
0
2 6 7
0
0
8 9 2
2 2 3
0
0
1 1
2
0
3
0
样例输出:
YES
NO
*/
/*
关键:
1 if(pSmallHead2 == NULL)//易错,这里小树为空必须放在前面,否则会出现大树也为空,但大树放在前面返回为假的情况
{
return true;
}
if(pBigHead1 == NULL)//递归出口
2 return judgeSubTree(pBigHead1->_left,pSmallHead2->_left) && judgeSubTree(pBigHead1->_right,pSmallHead2->_right);//两个根节点值相等,就返回左右子树的判定情况
3 if(pBigHead1->_iVal == pSmallHead2->_iVal)//如果头结点对应的值相同,则进行递归
{
bRes = judgeSubTree(pBigHead1,pSmallHead2);//递归主体
if(!bRes)
{
bRes = isSubTree(pBigHead1->_left,pSmallHead2);//递归调用左子树
}
if(!bRes)//这里,如果左子树如果找到,后面右子树就不需要查找了,因此需要加判定条件
*/
#include <stdio.h>
#include <string.h>
const int MAXSIZE = 1001;//1000个节点
typedef struct TreeNode
{
int _iVal;
struct TreeNode* _left;
struct TreeNode* _right;
}TreeNode;
TreeNode nodeArr1[MAXSIZE];
TreeNode nodeArr2[MAXSIZE];
int _iIndex1;
int _iIndex2;
TreeNode* createNode1()
{
++_iIndex1;
nodeArr1[_iIndex1]._left = nodeArr1[_iIndex1]._right = NULL;
return &nodeArr1[_iIndex1];
}
TreeNode* createNode2()
{
++_iIndex2;
nodeArr2[_iIndex2]._left = nodeArr2[_iIndex2]._right = NULL;
return &nodeArr2[_iIndex2];
}
//建大树
void buildBigTree(TreeNode** pHead,int* pArr,int n,TreeNode* (*pFun)())//如果刚开始就穿入的是空指针
{
if(pHead == NULL || *pHead == NULL || pArr == NULL || n <= 0)
{
return;
}
for(int i = 0 ; i < n ; i++)//先把各个单独的节点建立起来,其中首节点不需要建立,因为已经建立了
{
if(i)//建立其他节点
{
TreeNode* pNode = pFun();
pNode->_iVal = pArr[i];
//printf("%d ",pNode->_iVal);
}
else//对于头结点赋值
{
(*pHead)->_iVal = pArr[i];
}
}
//接下来构建二叉树的指向问题,共有n行,代表n个节点
for(int j = 1 ; j <= n; j++)
{
int iNum;//第1个节点表示,该节点有几个子节点
scanf("%d",&iNum);
int iLeft,iRight;
if(iNum == 0)//如果没有子节点,那么该节点无需设置,直接遍历下一个节点
{
continue;
}
else if(iNum == 1)//如果只有一个节点,默认为子节点编号左节点
{
scanf("%d",&iLeft);
nodeArr1[j]._left = &nodeArr1[iLeft];
}
else if(iNum == 2)//构建该节点的左右子节点
{
scanf("%d %d",&iLeft,&iRight);//接收子节点编号
nodeArr1[j]._left = &nodeArr1[iLeft];
nodeArr1[j]._right = &nodeArr1[iRight];
}
}
}
//建小树
void buildSmallTree(TreeNode** pHead,int* pArr,int n,TreeNode* (*pFun)())
{
if(pHead == NULL || *pHead == NULL || pArr == NULL || n <= 0)
{
return;
}
for(int i = 0 ; i < n ; i++)//先把各个单独的节点建立起来,其中首节点不需要建立,因为已经建立了
{
if(i)//建立其他节点
{
TreeNode* pNode = pFun();
pNode->_iVal = pArr[i];
}
else//对于头结点赋值
{
(*pHead)->_iVal = pArr[i];
}
}
//接下来构建二叉树的指向问题,共有n行,代表n个节点
for(int j = 1 ; j <= n; j++)
{
int iNum;//第1个节点表示,该节点有几个子节点
scanf("%d",&iNum);
int iLeft,iRight;
if(iNum == 0)//如果没有子节点,那么该节点无需设置,直接遍历下一个节点
{
continue;
}
else if(iNum == 1)//如果只有一个节点,默认为子节点编号左节点
{
scanf("%d",&iLeft);
nodeArr2[j]._left = &nodeArr2[iLeft];
}
else if(iNum == 2)//构建该节点的左右子节点
{
scanf("%d %d",&iLeft,&iRight);//接收子节点编号
nodeArr2[j]._left = &nodeArr2[iLeft];
nodeArr2[j]._right = &nodeArr2[iRight];
}
}
}
//主递归函数:设置头结点,对于头结点相同比较,一旦发现两者从头结点向下遍历。
//判断是否具有相同子结构的次递归函数:若发现头结点的值不同,则立即返回。否则返回左子树的比较情况和右子树
//的比较情况。一旦小树变空,说明是正确的。大数变空则是错误的。(递归出口)
//因为这里是遍历,因此不需要用二级指针。只有当需要改变的时候采用二级指针
bool judgeSubTree1(TreeNode* pBigHead1,TreeNode* pSmallHead2)
{
if(pSmallHead2 == NULL)//易错,这里小树为空必须放在前面,否则会出现大树也为空,但大树放在前面返回为假的情况
{
return true;
}
if(pBigHead1 == NULL)//递归出口
{
return false;
}
if(pBigHead1->_iVal == pSmallHead2->_iVal)//两个根节点值相等,就返回左右子树的判定情况
{
return judgeSubTree1(pBigHead1->_left,pSmallHead2->_left) &&
judgeSubTree1(pBigHead1->_right,pSmallHead2->_right);
}
else
{
return false;
}
}
bool judgeSubTree(TreeNode* pBigHead1,TreeNode* pSmallHead2)
{
if(pSmallHead2 == NULL)//易错,这里小树为空必须放在前面,否则会出现大树也为空,但大树放在前面返回为假的情况
{
return true;
}
if(pBigHead1 == NULL)//递归出口
{
return false;
}
if(pBigHead1->_iVal != pSmallHead2->_iVal)
{
return false;
}
return judgeSubTree(pBigHead1->_left,pSmallHead2->_left) && judgeSubTree(pBigHead1->_right,pSmallHead2->_right);//两个根节点值相等,就返回左右子树的判定情况
}
bool isSubTree1(TreeNode* pBigHead1,TreeNode* pSmallHead2)
{
if(!pBigHead1 || !pSmallHead2)//设置递归出口
{
return false;
}
bool bRes = false;//默认为假
if(pBigHead1->_iVal == pSmallHead2->_iVal)//如果头结点对应的值相同,则进行递归
{
bRes = judgeSubTree1(pBigHead1,pSmallHead2);//递归主体
if(!bRes)
{
bRes = isSubTree1(pBigHead1->_left,pSmallHead2);//递归调用左子树
}
if(!bRes)//这里,如果左子树如果找到,后面右子树就不需要查找了,因此需要加判定条件
{
bRes = isSubTree1(pBigHead1->_right,pSmallHead2);//递归调用右子树
}
}
return bRes;
}
bool isSubTree(TreeNode* pBigHead1,TreeNode* pSmallHead2)
{
bool bRes = false;//默认为假
if(pBigHead1 && pSmallHead2)//设置递归出口
{
if(pBigHead1->_iVal == pSmallHead2->_iVal)//如果头结点对应的值相同,则进行递归
{
bRes = judgeSubTree(pBigHead1,pSmallHead2);//递归主体
if(!bRes)
{
bRes = isSubTree(pBigHead1->_left,pSmallHead2);//递归调用左子树
}
if(!bRes)//这里,如果左子树如果找到,后面右子树就不需要查找了,因此需要加判定条件
{
bRes = isSubTree(pBigHead1->_right,pSmallHead2);//递归调用右子树
}
}
}
return bRes;
}
void process()
{
int n,m;
while(EOF != scanf("%d %d",&n,&m))
{
if(n <= 0 || n > 1000 || m <= 0 || m > 1000 || n < m)
{
printf("NO\n");
continue;
}
//建大树
int iArr1[MAXSIZE];
_iIndex1 = 0;//注意结点编号从1开始,注意记录头结点
memset(nodeArr1,NULL,sizeof(nodeArr1));
TreeNode* head1 = createNode1();//不能穿入空指针
TreeNode** pHead1 = &head1;
for(int i = 0 ; i < n ; i++)//接受树A的n各节点的值,这里应该顺便将各个节点创建出来
{
scanf("%d",&iArr1[i]);
}
buildBigTree(pHead1,iArr1,n,createNode1);
//建小树
int iArr2[MAXSIZE];
_iIndex2 = 0;//注意结点编号从1开始,注意记录头结点
memset(nodeArr2,NULL,sizeof(nodeArr2));
TreeNode* head2 = createNode2();//不能穿入空指针
TreeNode** pHead2 = &head2;
for(int j = 0 ; j < m ; j++)//接受树A的n各节点的值,这里应该顺便将各个节点创建出来
{
scanf("%d",&iArr2[j]);
}
buildSmallTree(pHead2,iArr2,m,createNode2);
//对已经建立的树,判断大树是否包含小树的子结构
bool bRes = isSubTree(head1,head2);
if(bRes)
{
printf("YES\n");
}
else
{
printf("NO\n");
}
}
}
int main(int argc,char* argv[])
{
process();
getchar();
return 0;
}