在这里占个坑,记录一下学习数据结构中例题
目录
二叉树的同构
1.建树
首先理清题目意思,给出的每组数据中,仅仅表现了父节点的内容与子节点的编号的关系,无法确定根节点。
这里用结构体数组来储存树,根据数组的下标,则可确定当前节点的内容与孩子节点的位置。找到根节点的位置后,整棵树的架构便明确下来。
根节点的特殊之处在于,根节点是是无人指向的。
为了找出根节点,可以使用一个初值全为0的数组,若数组下标被指向,则反转为1。
2.判断是否为同构
设置一个递归函数(目的:判断两树是否同构),传入两棵树。
同构操作的实质是什么?我认为是交换左右子树。镜像对称的两树是同构的特殊情况,而同构是从上到下,逐渐深入,每次交换(或不交换)左右子树。
分情况进行递归的选择:
两树都为空:则一定是同构的。
只有一个是空的,或者根节点的元素不一样,一定不同构。
两个左子树都为空,则判断两右子树是否同构。(这里的左右不必纠结,也没有特殊性)
接下来判断根节点的左右节点,若左等于左,右等于右,则说明在这一步是相同的,接下来进行的操作是传入递归,比较分别比较两个左子树、两个右子树是否同构。若在根节点那一步,发生了左右交换,则比较左树的右子树和有树的左子树、左数的左和右数的右是否同构。
代码:
#include <iostream>
using namespace std;
#define maxsize 11
#define Null -1
typedef int Tree;
struct TNode {
char data;
Tree left,right;
}T1[maxsize], T2[maxsize];
Tree BuildTree(struct TNode T[]){
int N,root;
char l,r;
bool isroot[maxsize];
root = Null;
scanf("%d", &N);
if(N) {
for(int i = 0; i < N; ++i) isroot[i] = true;
getchar();
for(int i = 0; i < N; ++i) {
scanf("%c %c %c", &T[i].data, &l, &r);
getchar();
if(l != '-') {
T[i].left = l -'0';
isroot[T[i].left] = false;
} else T[i].left = Null;
if(r != '-') {
T[i].right = r -'0';
isroot[T[i].right] = false;
} else T[i].right = Null;
}
for(int i = 0; i < N; ++i)
if(isroot[i]) {
root = i;
break;
}
}
return root;
}
bool judge(Tree R1, Tree R2) {
if(R1 == Null && R2 == Null)
return true; //两个都为空
if((R1 == Null && R2 != Null) || (R1 != Null && R2 == Null))
return false; //其中一个为空
if(T1[R1].data != T2[R2].data)
return false; //根节点元素就不一样
//两个左子树都为空,则判断右子树是否同构
if(T1[R1].left == Null && T2[R2].left == Null)
return judge(T1[R1].right, T2[R2].right);
//两个左子树都不为空且元素相同,不用交换
if((T1[R1].left != Null) && (T2[R2].left != Null)
&& (T1[T1[R1].left].data == T2[T2[R2].left].data))
return judge(T1[R1].left, T2[R2].left) &&
judge(T1[R1].right, T2[R2].right);
else {//交换左右子树,判断
return judge(T1[R1].left, T2[R2].right) &&
judge(T1[R1].right, T2[R2].left);
}
}
int main() {
Tree R1, R2;
R1 = BuildTree(T1);
R2 = BuildTree(T2);
if(judge(R1, R2))
cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
3.同类型题目
层次遍历——使用队列实现
首先根节点入队,执行循环
{
pop队列中队头的元素
判断此元素左右孩子是否为空,将其不空的左右孩子入队
}
是否同一棵二叉搜索树
思路
给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。
这里采取的方法是先根据第一行的输入建立一棵树T,再判断序列与树是否同源。
首先在书中寻找序列的某个元素,看寻找的路径中,是否有新出现的节点。(也就是说这个序列中,如果不事先知道这个节点的值的话,是不可能得到像T一般的树的)
因此需要设置标志,判断是否已经访问过了。
代码
#include <iostream>
using namespace std;
#define maxsize 11
typedef struct TNode* Tree;
struct TNode {
int data;
Tree left,right;
int flag; //判断是否访问过
};
void Clear(Tree R) { //清除标记
if(!R) return;
R->flag = 0;
Clear(R->left);
Clear(R->right);
}
void FreeTree(Tree R) { //清空该树
if(!R) return;
FreeTree(R->left);
FreeTree(R->right);
delete R;
}
Tree NewNode(int data) {//
Tree R = new TNode;
R->data = data;
R->left = R->right = NULL;
R->flag = 0;
return R;
}
Tree BST_Insert(int data, Tree R) {
if (!R) R = NewNode(data);
else {
if(data > R->data) //大于该结点,插入到右子树
R->right = BST_Insert(data, R->right);
else //小于或等于该结点,插入到左子树
R->left = BST_Insert(data, R->left);
}
return R;
}
Tree Build(int N) {
Tree R = NULL;
int x;
cin >> x;
R = NewNode(x);
for(int i = 1; i < N; ++i) {
cin >> x;
R = BST_Insert(x, R);
}
return R;
}
bool check(int data, Tree R) {
if(R->flag) {//已经访问过了
if(data < R->data)
return check(data, R->left);
else if(data > R->data)
return check(data, R->right);
else return false;
} else {
if(data == R->data) {
R->flag = 1;
return true;
} else return false;
}
}
bool judge(Tree R1, int N) {
int x;
bool flag = true;
if(N && R1) {
cin >> x;
if(x != R1->data) flag = false;
R1->flag = 1;
for(int i = 1; i < N; ++i) {
cin >> x;
if(flag && (!check(x,R1))) flag = false;
}
}
return flag;
}
int main() {
int N, L;
cin >> N;
while(N) {
cin >> L;
Tree R1;
R1 = Build(N);
for(int i = 0; i < L; ++i) {
if(judge(R1, N))
cout << "Yes" << endl;
else cout << "No" << endl;
Clear(R1); //清除标记
}
FreeTree(R1);
cin >> N;
}
return 0;
}