这是我有史以来做过最难的数据结构,没查找任何资料写的题目,我尽量备注清楚方便大家读懂。
///*题目:树与二叉树的相互转换
///*作者:仲舟
///*难度:★★★★★★
///*完成时间:2021.05.10
#include<stdio.h>
#define M 3//树的度数
#define MAXSIZE 100//最大结点数
typedef struct bnode { /*二叉树结构定义*/
char data;
struct bnode *lchild,*rchild;
} binnode;
typedef binnode *bintree;
typedef struct tnode {/*树的结构定义*/
char data;
struct tnode *child[M];
} node;
typedef node *tree;
tree CreateTree();/*按前序遍历顺序建立一棵树,返回树根地址*/
bintree CreateBinTree();/*按前序遍历顺序建立一棵二叉树,返回树根地址*/
bintree FromTreeToBintree(tree t);/*将树转化为二叉树*/
tree FromBintreeToTree(bintree bt);/*将二叉树转化为树*/
void PreOrderTree(tree t);/*打印树*/
void PreOrderBinTree(bintree bt);/*打印二叉树*/
int main() {
printf("这里是仲舟的树与二叉树转化程序\n");
tree a;//a是普通树
bintree b;//b是二叉树
/*1.树转二叉树*/
printf("请用前序遍历的方法输入度数为3的树:");
a=CreateTree();//创建a树
getchar();//读取回车
printf("此树前序遍历为:");
PreOrderTree(a);//输出a前序遍历
printf("\n");
b=FromTreeToBintree(a);//将a树转化为二叉树b
printf("转换成二叉树后为:");
PreOrderBinTree(b);//输出b树
printf("\n");
/*2.二叉树转树*/
printf("请用前序遍历的方法输入二叉树树:");
b=CreateBinTree();
getchar();//读取回车
printf("此树前序遍历为:");
PreOrderBinTree(b);
printf("\n");
a=FromBintreeToTree(b);
printf("转换成树后为:");
PreOrderTree(a);
printf("\n");
return 0;
}
tree CreateTree() {
int i;
char ch;
tree t;
if ((ch=getchar())=='#') t=NULL;
else {
t=(tree) malloc (sizeof(node));
t->data=ch;
for (i=0; i<M; ++i)
t->child[i]= CreateTree();
}
return t;
}
bintree CreateBinTree() {
char ch;
bintree t;
if ((ch=getchar())=='#') t=NULL;
else {
t=(bintree)malloc(sizeof(binnode));
t->data=ch;
t->lchild=CreateBinTree();
t->rchild=CreateBinTree();
}
return t;
}
bintree FromTreeToBintree(tree t) {
/*仲舟提示:在树转二叉树的时候,由于二叉树左边孩子,右边兄弟,如果要访问兄弟,就不能用递归,
和中序遍历一样使用循环+队列的形式,因为访问兄弟需要把父亲先存起来*/
/*1.声明变量*/
tree tqueue[MAXSIZE],tnow;//队列,正在处理的结点
bintree bqueue[MAXSIZE],bt,bnow,blast;//同步队列(和前面的队列同步,建立这个是后续需要引用此类型的指针,存储内容和前队列一样),新生二叉树的根,正在处理的结点,上一个处理的结点(类似链表,要连接就要两个结点,所以要保存前后数据)
int l=0,r=0;//队列最左下标,队列最右下标
/*2.队列初始化*/
bt=(bintree)malloc(sizeof(binnode));//开辟空间
bt->data=t->data;//新建树的根
bt->lchild=NULL;//左孩子初始化
bt->rchild=NULL;//右孩子初始化
tqueue[r]=t;//树根入队
bqueue[r]=bt;//树根入队
r++;
/*3.开始转化*/
while(l<r) { //队列还有结点
tnow=tqueue[l];//开始处理最左边的结点
blast=bqueue[l];//上一个处理的结点
l++;//左下标右移
int flag=0;//第一个孩子标记
for(int i=0; i<M; i++) //开始连接孩子
if(tnow->child[i]!=NULL) {//如果有孩子
/*1.开辟空间*/
bnow=(bintree)malloc(sizeof(binnode));//【新建】开辟空间
bnow->data=tnow->child[i]->data;//【赋值】拷贝
bnow->lchild=NULL;//左孩子初始化
bnow->rchild=NULL;//右孩子初始化
/*2.分类讨论*/
if(flag==0) { //对二叉来说,如果他还没有孩子的时候,这个孩子就是now的左孩子
blast->lchild=bnow;//【连接1】与上一个结点相连
flag=1;//对树来说,第一个孩子已出现
} else { //对二叉来说,如果他有孩子了,这个孩子就是那个孩子的右孩子
blast->rchild=bnow;//【连接2】与上一个结点相连
}
/*3.记忆结点*/
blast=bnow;//处理完了,这个就是上一个结点
tqueue[r]=tnow->child[i];//它的孩子入队
bqueue[r]=bnow;//同步入队
r++;//右下标右移
}
}
return bt;
}
tree FromBintreeToTree(bintree bt) {
/*仲舟提示:根据上述函数逆向思维,一个二叉树结点的左、右、右、右……孩子就是树的所有孩子*/
bintree bqueue[MAXSIZE],bnow;//队列,正在处理的结点
tree tqueue[MAXSIZE],t,tnow,tlast;//同步队列(和前面的队列同步,建立这个是后续需要引用此类型的指针,存储内容和前队列一样),新生树的根,正在处理的结点,上一个处理的结点(类似链表,要连接就要两个结点,所以要保存前后数据)
int l=0,r=0;//队列最左下标,队列最右下标
/*2.队列初始化*/
t=(tree)malloc(sizeof(node));//开辟空间
t->data=bt->data;//新建树的根
for(int i=0; i<M; i++)//孩子初始化
t->child[i]=NULL;
tqueue[r]=t;//树根入队
bqueue[r]=bt;//树根入队
r++;
/*3.开始转化*/
while(l<r) { //队列还有结点
bnow=bqueue[l];//开始处理最左边的结点
tlast=tqueue[l];//上一个处理的结点(父结点)
l++;//左下标右移
for(int i=0; i<M; i++)//父节点最多M个孩子
if(i==0&&bnow->lchild||i!=0&&bnow->rchild) {//如果树有孩子的条件(即二叉树有左、右、右……孩子)
/*1.开辟空间*/
tnow=(tree)malloc(sizeof(node));//【新建】开辟空间
for(int j=0; j<M; j++)//孩子初始化
tnow->child[j]=NULL;
/*2.分类讨论*/
if(i==0) {//如果是树的第一个孩子,那么是二叉树的左孩子
tnow->data=bnow->lchild->data;//【赋值1】拷贝
bnow=bnow->lchild;//处理完了,这个就是上一个结点
} else {//如果是树的第二、三个孩子,那么是二叉树左孩子的右孩子的右孩子……
tnow->data=bnow->rchild->data;//【赋值2】拷贝
bnow=bnow->rchild;//处理完了,这个就是上一个结点
}
tlast->child[i]=tnow;//【连接】与父结点连接
/*3.记忆结点*/
tqueue[r]=tnow;//它的孩子入队
bqueue[r]=bnow;//同步入队
r++;//右下标右移
}
}
return t;
}
void PreOrderTree(tree t) { /* t为指向树根结点的指针,输出树的前序遍历序列*/
if(t) { //如果不为空
printf("%c",t->data);//先输出老子
for(int i=0; i<=M-1; i++) //再找孩子
if(t->child[i])//如果有孩子
PreOrderTree(t->child[i]);
}
//如果空,则终止递归
}
void PreOrderBinTree(bintree bt) { /* t为指向树根结点的指针,输出树的前序遍历序列*/
if(bt) { //如果不为空
printf("%c",bt->data);//先输出老子
if(bt->lchild)//如果有孩子
PreOrderBinTree(bt->lchild);
if(bt->rchild)//如果有孩子
PreOrderBinTree(bt->rchild);
}
//如果空,则终止递归
}
仲舟原创,未经允许禁止转载!