根据二叉树的先序遍历结果创建一棵二叉树,即先创建根结点,然后再创建左子树,最后创建右子树,对于左右子树的创建也遵循根左右的原则,所以对于左右子树的创建可以递归调用本函数,此问题是典型的需要用递归算法求解的问题,关于递归算法不了解的可以看我上一篇博文http://t.csdn.cn/YDhB4
在写代码的过程中可以加入一些提示性的内容,让用户知道该怎么进行输入结点元素的值 。算法实现代码:
//使用递归算法,创建二叉树
void F_CreateBitree(Bitree root)
{
//先序创建,遵循先创建根结点,再创建左子树最后在创建右子树,创建左右子树的时候递归调用此算法
//输入根结点元素的值
printf("请输入根结点的值\n");
scanf("%c", &(root->data));
getchar();
//递归出口是输入#
if (root->data == '#')
{
return;
}
//创建根结点元素的值
//创建左子树,左子树递归调用本函数
printf("请输入%c的左子树的根结点的值\n",root->data);
root->lchild = (Bitree)malloc(sizeof(BitreeNode));
F_CreateBitree(root->lchild);
//创建右子树,右子树递归调用本函数
printf("请输入%c的右子树的根节点的值\n",root->data);
root->rchild = (Bitree)malloc(sizeof(BitreeNode));
F_CreateBitree(root->rchild);
if ((root->lchild)->data == '#')
{
root->lchild = NULL;
}
if ((root->rchild)->data == '#')
{
root->rchild = NULL;
}
}
创建完成该二叉树之后要对该二叉树进行中序遍历,下面实现二叉树的中序遍历的算法是非递归的,用到了栈的知识,我在上一篇博文写了使用递归算法实现先序遍历,中序遍历,后序遍历二叉树的算法,感兴趣的话可以去看看http://t.csdn.cn/NLff5
利用栈来实现二叉树的中序遍历的非递归算法,算法思路:
中序遍历的思想就是先遍历左子树,然后访问根结点,最后遍历右子树,对于左右子树的遍历也遵循左根右的原则,所以其思想就是沿着左子树深入,直到到达左子树为空的结点位置,此时将此节点的元素进行输出,即进行出栈操作,然后再访问该节点的右子树,访问完成右子树之后需要返回该结点的上一个结点 ,其实这一步操作是通过退栈操作来完成的。以下是实现算法的源代码:
void MiddleOrderTraverse(Bitree root,Ls mystack)
{
//中序遍历,先遍历左子树,在遍历根结点,最后遍历右子树,对左右子树的遍历也遵循左根右的原则
//利用栈来实现中序遍历的非递归算法
Bitree m = root;
//先判断二叉树是不是为空
if (!root)
{
return;
}
while (1)
{
//根结点入栈
Bitree temp;
if (m)
{
InsertStack(mystack, m);
m = m->lchild; //先沿着左子树深入直到到达左子树为空的结点
}
else
{
temp = PopStack(mystack);
//当到达左子树为空的结点,将当前结点元素进行退栈,即输出当前结点的值,然后继续访问当前节点的右子树。
//整个过程是不断重复地过程即沿着左子树深入直至为空,然会输出当前栈顶元素的值,然会返回上一结点元素,然后继续访问当前节点的右子树,所以有后深入的先返回的特性,
//可以运用站的特性实现此算法,而实现返回上一个结点是通过退栈这一操作完成的
printf("%c\t", temp->data);
m = temp->rchild;
}
if ((mystack->top==mystack->base) && m == NULL)
{
break;
}
}
}
既然要用到栈,那么就要先实现栈的定义,以及定义实现相关操作的接口,站的定义以及相关操作的实现的源代码如下:
//定义链栈结点
typedef struct LinkStackNode
{
Bitree node;
struct LinkStackNode* next;
}LinkStackNode,*LsNode;
//定义链栈
typedef struct Linkstack
{
LsNode top; //定义栈顶指针,栈顶是允许插入和删除的一端
LsNode base; //定义栈底指针,是不允许进行插入和删除元素的一端
}LinkStack,*Ls;
//创建一个空栈
void InitStack(Ls mystack);
//向栈中插入元素e
void InsertStack(Ls mystack, Bitree e); //向栈中插入元素不存在栈满问题但存在栈空问题,只能在栈顶处插入元素
//若栈不空则删除栈顶元素,并将栈顶元素的值作为函数返回值返回
Bitree PopStack(Ls mystack);
void InitStack(Ls mystack)
{
mystack->top = mystack->base = (LsNode)malloc(sizeof(LinkStackNode));
mystack->base->next = NULL;
printf("创建空栈成功\n");
}
void InsertStack(Ls mystack, Bitree e)
{
LsNode p = (LsNode)malloc(sizeof(LinkStackNode));
if (!p)
{
printf("ERROR:申请内存失败\n");
return;
}
p->node = e;
p->next = mystack->top;
mystack->top = p;
}
Bitree PopStack(Ls mystack)
{
if (mystack->top->next != NULL)
{
LsNode temp;
Bitree t;
temp = mystack->top;
t = temp->node;
mystack->top = mystack->top->next;
free(temp);
return t;
}
return NULL;
}
完整程序源代码以及运行效果截图
程序源代码:
//根据二叉树的先序遍历结果来创建一棵二叉树,并对其进行先序,中序,后序遍历输出,遍历时用到栈的知识以实现非递归算法;
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
//定义树节点
typedef struct BitreeNode
{
char data; //数据域用于存放结点元素的值,节点元素的值为26个英文小写字母
struct BitreeNode* lchild, * rchild; //指针域,分别用于存放指向左右孩子的结点的指针
}BitreeNode,*Bitree;
//定义链栈结点
typedef struct LinkStackNode
{
Bitree node;
struct LinkStackNode* next;
}LinkStackNode,*LsNode;
//定义链栈
typedef struct Linkstack
{
LsNode top; //定义栈顶指针,栈顶是允许插入和删除的一端
LsNode base; //定义栈底指针,是不允许进行插入和删除元素的一端
}LinkStack,*Ls;
//按照先序遍历结果创建一棵二叉树
void F_CreateBitree(Bitree root);
void MiddleOrderTraverse(Bitree root, Ls mystack); //中序遍历
//创建一个空栈
void InitStack(Ls mystack);
//向栈中插入元素e
void InsertStack(Ls mystack, Bitree e); //向栈中插入元素不存在栈满问题但存在栈空问题,只能在栈顶处插入元素
//若栈不空则删除栈顶元素,并将栈顶元素的值作为函数返回值返回
Bitree PopStack(Ls mystack);
int main()
{
LinkStack ls;
Ls mystack = &ls;
BitreeNode Bi;
Bitree mytree = &Bi;
//先序创建二叉树
F_CreateBitree(mytree);
//验证是否先序创建成功即先序遍历此二叉树看是否与我们再创建它时输入的节点序列是否一样;
//创建一个空栈;
InitStack(mystack);
//printf("先序遍历结果:\n");
//FirstOrderTraverse(mytree, mystack);
printf("中序遍历结果:\n");
MiddleOrderTraverse(mytree,mystack);
//printf("后序遍历结果:\n");
//PostOrderTraverse(mytree, mystack);
return 0;
}
//使用递归算法,创建二叉树
void F_CreateBitree(Bitree root)
{
//先序创建,遵循先创建根结点,再创建左子树最后在创建右子树,创建左右子树的时候递归调用此算法
//输入根结点元素的值
printf("请输入根结点的值\n");
scanf("%c", &(root->data));
getchar();
//递归出口是输入#
if (root->data == '#')
{
return;
}
//创建根结点元素的值
//创建左子树,左子树递归调用本函数
printf("请输入%c的左子树的根结点的值\n",root->data);
root->lchild = (Bitree)malloc(sizeof(BitreeNode));
F_CreateBitree(root->lchild);
//创建右子树,右子树递归调用本函数
printf("请输入%c的右子树的根节点的值\n",root->data);
root->rchild = (Bitree)malloc(sizeof(BitreeNode));
F_CreateBitree(root->rchild);
if ((root->lchild)->data == '#')
{
root->lchild = NULL;
}
if ((root->rchild)->data == '#')
{
root->rchild = NULL;
}
}
void InitStack(Ls mystack)
{
mystack->top = mystack->base = (LsNode)malloc(sizeof(LinkStackNode));
mystack->base->next = NULL;
printf("创建空栈成功\n");
}
void InsertStack(Ls mystack, Bitree e)
{
LsNode p = (LsNode)malloc(sizeof(LinkStackNode));
if (!p)
{
printf("ERROR:申请内存失败\n");
return;
}
p->node = e;
p->next = mystack->top;
mystack->top = p;
}
Bitree PopStack(Ls mystack)
{
if (mystack->top->next != NULL)
{
LsNode temp;
Bitree t;
temp = mystack->top;
t = temp->node;
mystack->top = mystack->top->next;
free(temp);
return t;
}
return NULL;
}
void MiddleOrderTraverse(Bitree root,Ls mystack)
{
//中序遍历,先遍历左子树,在遍历根结点,最后遍历右子树,对左右子树的遍历也遵循左根右的原则
//利用栈来实现中序遍历的非递归算法
Bitree m = root;
//先判断二叉树是不是为空
if (!root)
{
return;
}
while (1)
{
//根结点入栈
Bitree temp;
if (m)
{
InsertStack(mystack, m);
m = m->lchild; //先沿着左子树深入直到到达左子树为空的结点
}
else
{
temp = PopStack(mystack);
//当到达左子树为空的结点,将当前结点元素进行退栈,即输出当前结点的值,然后继续访问当前节点的右子树。
//整个过程是不断重复地过程即沿着左子树深入直至为空,然会输出当前栈顶元素的值,然会返回上一结点元素,然后继续访问当前节点的右子树,所以有后深入的先返回的特性,
//可以运用站的特性实现此算法,而实现返回上一个结点是通过退栈这一操作完成的
printf("%c\t", temp->data);
m = temp->rchild;
}
if ((mystack->top==mystack->base) && m == NULL)
{
break;
}
}
}
运行效果截图: