PTA 7-3 树的同构 (25分)

给定两棵树T1和T2。如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是“同构”的。例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A、B、G的左右孩子互换后,就得到另外一棵树。而图2就不是同构的。

图1

图2

现给定两棵树,请你判断它们是否是同构的。

输入格式:

输入给出2棵二叉树树的信息。对于每棵树,首先在一行中给出一个非负整数N (≤10),即该树的结点数(此时假设结点从0到N−1编号);随后N行,第i行对应编号第i个结点,给出该结点中存储的1个英文大写字母、其左孩子结点的编号、右孩子结点的编号。如果孩子结点为空,则在相应位置上给出“-”。给出的数据间用一个空格分隔。注意:题目保证每个结点中存储的字母是不同的。

输出格式:

如果两棵树是同构的,输出“Yes”,否则输出“No”。

输入样例1(对应图1):

8
A 1 2
B 3 4
C 5 -
D - -
E 6 -
G 7 -
F - -
H - -
8
G - 4
B 7 6
F - -
A 5 1
H - -
C 0 -
D - -
E 2 -

输出样例1:

Yes

输入样例2(对应图2):

8
B 5 7
F - -
A 0 3
C 6 -
H - -
D - -
G 4 -
E 1 -
8
D 6 -
B 5 -
E - -
H - -
C 0 2
G - 3
F - -
A 1 4

输出样例2:

No

解法一:

此题可以使用树的孩子表示法,注意这里可以不使用链表,而直接可以通过数组的下标来确定父亲与孩子之间的关系。

建立结构体:

struct Node{
    char alpha;
    int lchild;
    int rchild;
}tree1[15],tree2[15];

 其中alpha表示结点的值,lchild表示左孩子,rchild表示右孩子。

第一种方法我是通过遍历第一个树,然后从第二个树中找到和第一个树相同的结点,再比较它们的子结点是否相同,如果不同则直接打印No并退出,如果相同则继续遍历。这里有一个特殊情况就是当树的结构相同但数值不同时,返回的是“No”。

代码如下:

#include<iostream>
#include<stdio.h>
using namespace std;
struct Node{
    char alpha;
    int lchild;
    int rchild;
}tree1[15],tree2[15];

int main()
{
    int n1,n2;//结点数
    cin >> n1;
    char a,b,c;
    for(int i = 0;i < n1;i++)
    {
        cin >> a >> b >> c;
        tree1[i].alpha = a;
        if(b!='-') tree1[i].lchild = b - '0';
        else tree1[i].lchild = -1;
        if(c!='-') tree1[i].rchild = c - '0';
        else tree1[i].rchild = -1;
    }
    cin >> n2;
    for(int i = 0;i < n2;i++)
    {
        cin >> a >> b >> c;
        tree2[i].alpha = a;
        if(b!='-') tree2[i].lchild = b - '0';
        else tree2[i].lchild = -1;
        if(c!='-') tree2[i].rchild = c - '0';
        else tree2[i].rchild = -1;
    }
    if(n1 != n2)
    {
        cout << "No\n";
        return 0;
    }

    char temp1,temp2,temp3,temp4;
    bool find_out = false;

    for(int i = 0;i < n1;i++)
    {
        if(tree1[i].lchild != -1)
        {
            temp1 = tree1[tree1[i].lchild].alpha;
        }
        else
        {
            temp1 = '-';
        }

        if(tree1[i].rchild != -1)
        {
            temp2 = tree1[tree1[i].rchild].alpha;
        }
        else
        {
            temp2 = '-';
        }

        for(int j = 0;j < n2;j++)
        {
            if(tree2[j].alpha == tree1[i].alpha)
            {
                find_out = true;
                if(tree2[j].lchild!=-1)
                {
                    temp3 = tree2[tree2[j].lchild].alpha;
                }
                else
                {
                    temp3 = '-';
                }
                if(tree2[j].rchild!=-1)
                {
                    temp4 = tree2[tree2[j].rchild].alpha;
                }
                else
                {
                    temp4 = '-';
                }

                if(!((temp1==temp3&&temp2==temp4)||(temp1==temp4&&temp2==temp3)))
                {
                    cout << "No" << endl;
                    return 0;
                }
            }
        }
        if(!find_out)
        {
            cout << "No" << endl;
            return 0;
        }

    }
    cout << "Yes" << endl;

    return 0;
}

解法二:

这种方法就比较简单,先和第一种方法一样使用一个结构体数组来表示树,然后在建树的时候找到树的根结点并保存下来(寻找根结点的方法就是寻找在树的左孩子和右孩子中没有出现的数);找到根结点后可以通过从根结点往下逐次比较。这里有三种情况:

  • 判断根结点是否为空,若两个根结点都为空则是同构树;如果一个为空一个不为空则不是同构树
  • 两个根结点都存在,但是对应的数值不同,不是同构树
  • 判断两个根结点的左右子树是否相同或者是否是顺序调换。

注意上面三种情况是通过递归来判断的。

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
#define NULL -1
struct Node
{
	char alpha;
	int lchild;
	int rchild;
}tree1[15], tree2[15];

//建树并返回树的根结点
int BuildTree(struct Node tree[])
{
	int n;
	int label[15];
	char data, left, right;
	memset(label, 0, sizeof(label));//初始化
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cin >> data >> left >> right;
		tree[i].alpha = data;
		if (left != '-')
		{
			tree[i].lchild = left - '0';
			label[left - '0'] = 1;
		}
		else
			tree[i].lchild = NULL;
		if (right != '-')
		{
			tree[i].rchild = right - '0';
			label[right - '0'] = 1;
		}
		else
			tree[i].rchild = NULL;
	}
	int root = NULL;//初始化根结点

	for (int i = 0; i < n; i++)
	{
		if (!label[i]) root = i;
	}
	return root;
}
bool judge(int root1, int root2)
{
	if (root1 == NULL && root2 == NULL) {//判断根节点是否都为空
		return true;//都为空则相同
	}
	if (root1 == NULL && root2 != NULL || root1 != NULL && root2 == NULL) {
		return false;//一个根节点为空另外一个不为空则为假
	}
	if (tree1[root1].alpha != tree2[root2].alpha) {
		return false;//两棵树根节点都存在,但数值不同则为假
	}
	if (tree1[tree1[root1].lchild].alpha == tree2[tree2[root2].lchild].alpha) {
		//如果左子树的值相等则判断右子树
		return judge(tree1[root1].rchild, tree2[root2].rchild);
	}
	else {
		//否则判断是否第二棵树是在第一课树左右子树调换之后得到的
		return judge(tree1[root1].lchild, tree2[root2].rchild)
			&& judge(tree1[root1].rchild, tree2[root2].lchild);
	}
}

int main()
{
	int root1 = BuildTree(tree1);
	int root2 = BuildTree(tree2);
	if (judge(root1, root2))
		cout << "Yes\n";
	else
		cout << "No\n";

	return 0;

}

发布了80 篇原创文章 · 获赞 101 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/BigDream123/article/details/104167884