总算可以不依据打草稿推过程来写了,完全根据自己的思路来。
包括 *创建一颗二叉树 *前中后序递归与非递归层次遍历二叉树。
这个树长这样,输入要保证把左右孩子都写上,没有就是#
救命,图怎么传不上来啊
#include<stdio.h>
#include<malloc.h>
#include<string.h>
struct tree //二叉树的结构体
{
char data;
struct tree *lchild,*rchild;
};
typedef struct tree tree;
typedef tree * bintree;
struct seqstack //栈的结构体
{
bintree data[100];
int top; //栈顶标记
int tag[100]; //后序遍历时的标记
};
typedef struct seqstack seqstack;
void push(seqstack *s,bintree t) //入栈
{
s->data[++s->top] = t;
}
bintree pop(seqstack *s) //出栈
{
return s->data[s->top--];
}
创建一棵树
输入序列:ABD###CEG###F##
bintree createtree()
{
char ch;bintree t;
if((ch = getchar()) == '#') t = NULL; //
else
{
t = (bintree)malloc(sizeof(tree));
t->data = ch;
t->lchild = createtree();
t->rchild = createtree();
}
return t;
}
在遍历过程中每一个结点会遇到3次
第一次遇到就输出是前序遍历
前序遍历 根–左--右
void preorder1(bintree t) //递归前序遍历
{
if(t)
{
printf("%5c",t->data);
preorder1(t->lchild);
preorder1(t->rchild);
}
}
void preorder2(bintree t) //非递归前序遍历
{
seqstack s;s.top = -1;
while(t || s.top != -1)
{
if(t)
{
printf("%5c",t->data); //第一次遇到输出
push(&s,t); //输出
t = t->lchild; //左子树
}
else
{
t = pop(&s);
t = t->rchild; //右子树
}
}
}
第二次遇到就输出中序遍历
中序遍历
void inorder1(bintree t) //递归的中序遍历
{
if(t)
{
inorder1(t->lchild);
printf("%5c",t->data);
inorder1(t->rchild);
}
}
void inorder2(bintree t) //非递归中序遍历
{
seqstack s;s.top = -1; //利用栈,没有元素时top = -1
while(t || s.top != -1)
{
if(t)
{
push(&s,t); //第一次遇到,不输出直接进栈
t = t->lchild; //根--左
}
else
{
t = pop(&s);
printf("%5c",t->data); //第二次遇到,输出
t = t->rchild;
}
}
}
第三次遇到输出后序遍历,左–右--根
后序遍历中第二次遇到根之后,要处理根节点的右子树,处理完后将根节点的tag标记为1,表示右结点处理完了。
而为什么要用tag可以看中序遍历中,第二次遇到根结点时,根节点是直接出栈的,但显然我这时候出栈能得到右结点,但右结点遍历完后不能再得到根结点,所以需要tag。
后序遍历
void postorder1(bintree t) //递归后序遍历
{
if(t)
{
postorder1(t->lchild);
postorder1(t->rchild);
printf("%5c",t->data);
}
}
void postorder2(bintree t) //非递归后序遍历
{
seqstack s;s.top = 0;
while(t || s.top != 0)
{
if(t)
{
s.data[s.top] = t; //第一次遇到进栈
s.tag[s.top] = 0; //将tag标记为0
s.top++;
t = t->lchild;
}
else
if(s.tag[s.top-1] == 1) //当tag = 1时,说明左右子树都处理完了,现在处理根节点
{
t = s.data[--s.top]; //第三次遇到,第二次看下面的else
printf("%5c",t->data); //输出
t = NULL;
}
else //当tag = 0时,说明左子树处理完了,现在处理右子树
{
s.tag[s.top-1] = 1; //注意此时的结点t是上一个结点的左子树,s.top指向的当前这个左子树,我要将左子树的父亲结点标记为1,因此top要-1
t = s.data[s.top-1]; //第二次遇到
t = t->rchild; //处理右子树
}
}
}
层次遍历
用队列
将根节点A的左右子树BC放入队列
下一个输出是B,将B的子树继续放入队列,位置是C的后面
B后面是C(B已经没了),处理C的子树,入队,位置是B子树的后面,就是这样循环啦,直到对头尾碰上
void leval(bintree t)
{
bintree a[100]; //队列a
int head = 0,tail = 1; //头,尾
a[head] = t; //队头进来
while(head < tail) //头尾没碰到
{
t = a[head++]; //开始处理队头,同时头往后移,下次循环输出下一个头
printf("%5c",t->data);
if(t->lchild) //处理左子树
a[tail++] = t->lchild;
if(t->rchild)
a[tail++] = t->rchild; //处理右子树
}
}
调试代码
int main()
{
bintree root;
printf("创建树,请输入树的前序序列:\n");
root = createtree();
printf("\n递归前序遍历:\n");
preorder1(root);
printf("\n非递归前序遍历:\n");
preorder2(root);
printf("\n递归中序遍历:\n");
inorder1(root);
printf("\n非递归中序遍历\n");
inorder2(root);
printf("\n递归后序遍历\n");
postorder1(root);
printf("\n非递归后序遍历\n");
postorder2(root);
printf("\n层次遍历\n");
leval(root);
}
结果
嗷,第一次写这个玩意儿,操作有点还不熟,下次就啃根据前中序列得到树,或者前后,后中?还有什么同构?是否完全啥的,东西好多,awsl。