二叉树遍历C语言递归非递归六种算法.docx
《二叉树遍历C语言递归非递归六种算法.docx》由会员分享,可在线阅读,更多相关《二叉树遍历C语言递归非递归六种算法.docx(19页珍藏版)》请在冰豆网上搜索。
二叉树遍历C语言递归非递归六种算法
二叉树遍历c语言(递归-非递归)六种算法
作者:
日期:
HEBEIUNITEDUNIVERSITY
数据结构(双语)
——项目文档报告
用两种方式实现表达式自动计算
专
业:
班
级:
指导教师:
名:
学
号:
一、设计思想01
二、算法流程图02
三、源代码04
四、运行结果11
五、遇到的问题及解决11
六、心得体会12
一、设计思想
二叉树的遍历分为三种方式,分别是先序遍历,中序遍历和后序颯历。
先序遍历实现的顺序是:
根左右,中序遍历实现的是:
左根右,后续遍历实现的是:
左右根。
根据不同的算法分,又分为递归遍历和非递归遍历。
递归算法:
1.先序遍历:
先序遍历就是首先判断根结点是否为空,为空则停止遍历,不为空则将左子作为新的根结点重新进行上述判断,左子適历结朿后,再将右子作为根结点判断,直至结束。
到达每一个结点时,打印该结点数据,即得先序遍历结果。
2•中序遍历:
中序遍历是首先判断该结点是否为空,为空则结束,不为空则将左子作为根结点再进行判断,打印左子,然后打印二叉树的根结点,最后再将右子作为参数进行判断,打印右子,直至结朿。
3.后续遍历:
指针到达一个结点时,判断该结点是否为空,为空则停止遍历,不为空则将左子作为新的结点参数进行判断,打印左子。
左子判断完成后,将右子作为结点参数传入判断,打印右子。
左右子判断完成后打印根结点。
非递归算法:
1•先序適历:
首先建立一个栈,当指针到达根结点时,打印根结点,判断根结点是否有左子和右子。
有左子和右子的话就打印左子同时将右子入栈,将左子作为新的根结点进行判断,方法同上。
若当前结点没有左子,则直接将右子打印,同时将右子作为新的根结点判断。
若肖前结点没有右子,则打印左子,同时将左子作为新的根结点判断。
若当前结点既没有左子也没有右子,则当前结点为叶子结点,此时将从栈中岀栈一个元素,作为当前的根结点,打印结点元素,同时将当前结点同样按上述方法判断,依次进行。
直至当前结点的左右子都为空,且栈为空时,遍历结朿。
2.中序遍历:
首先建立一个栈,左义一个常量flag(flag为0或者1),用flag记录结点的左子是否去过,没有去过为0,去过为1,默认为0.首先将指针指向根结点,将根结点入栈,然后将指针指向左子,左子作为新的结点,将新结点入栈,然后再将指针指向当前结点的左子,直至左子为空,则指针返回,flag置1,出栈一个元素,作为当前结点,打印该结点,然后判断flag,flag为1则将指针指向当前结点右子,将右子作为新的结点,结点入栈,再次进行上面的判断,直至当前结点右子也为空,则再出栈一个元素作为当前结点,一直到结束,使得当前结点右子为空,且栈空,遍历结朿。
3.后续遍历:
首先建立两个栈,然后左义两个常量。
第一个为status,取值为0,1,2.0代表左右子都没有去过,1代表去过左子,2,代表左右子都去过,默认为0。
第二个常量为flag,取值为0或者1,0代表进左栈,1代表进右栈。
初始时指针指向根结点,判断根结点是否有左子,有左子则,将根结点入左栈,status置0,flag置0,若没有左子则判断结点有没有右子,有右子就把结点入右栈,status1^0,flagIt1,若左右子都没有,则打印该结点,并将指针指向空,此时判断flag,若flag为0,则从左栈出栈一个元素作为当前结点,重新判断:
若flag为1则从右栈出栈一个元素作为当前结点,重新判断左右子是否去过,若status为1,则判断该结点有没有右子,若有右子,则将该结点入右栈,status^1,flag置1,若没有右子,则打印当前结点,并将指针置空,然后再次判断flag。
若当前结点status为2,且栈为空,则遍历结束。
若指针指向了左子,则将左子作为当前结点,判断其左右子情况,按上述方法处理,直至遍历结朿。
二、算法流程图
图1二叉树的建立
用先序方法建立二叉树,为每个结点左义左右子,用0代表空,得到上述二叉树
图2非递归二叉树遍历先序
首先建立一个栈,当指针到达根结点时,打印根结点,判断根结点是否有左子和右子。
有左子和右子的话就打印左子同时将右子入栈,将左子作为新的根结点进行判断,方法同上。
若当前结点没有左子,则直接将右子打印,同时将右子作为新的根结点判断。
若当前结点没有右子,则打印左子,同时将左子作为新的根结点判断。
若当前结点既没有左子也没有右子,则当前结点为叶子结点,此时将从栈中出栈一个元素,作为当前的根结点,打印结点元素,同时将当前结点同样按上述方法判断,依次进行。
直至当前结点的左右子都为空,且栈为空时,遍历结束。
图3非递归二叉树遍历中序
中序遍历:
首先建立一个栈,立义一个常flag(flag为0或者1),用flag记录结点的左子是否去过,没有去过为0,去过为1,默认为0.首先将指针指向根结点,将根结点入栈,然后将指针指向左子,左子作为新的结点,将新结点入栈,然后再将指针指向当前结点的左子,直至左子为空,则指针返回,flag置1,出栈一个元素,作为当前结点,打印该结点,然后判断flag,flag为1则将指针指向当前结点右子,将右子作为新的结点,结点入栈,再次进行上而的判断,直至当前结点右子也为空,则再出栈一个元素作为当前结点,一宜到结
图4非递归二叉树遍历后序
首先建立两个栈,然后左义两个常量。
第一个为status,取值为0,1,2.0代表左右子都没有去过,1代表去过左子,2,代表左右子都去过,默认为0。
第二个常疑为flag,取值为0或者1,0代表进左栈,1代表进右栈。
初始时指针指向根结点,判断根结点是否有左子,有左子则,将根结点入左栈,status置0,flag置0,若没有左子则判断结点有没有右子,有右子就把结点入右栈,status0,flag巻1,若左右子都没有,则打印该结点,并将指针指向空,此时判断flag,若flag为0,则从左栈出栈一个元素作为当前结点,重新判断;若flag为1则从右栈岀栈一个元素作为当前结点,重新判断左右子是否去过,若status为1,则判断该结点有没有右子,若有右子,则将该结点入右栈,status1,flag置1,若没有右子,则打印当前结点,并将指针置空,然后再次判断flag。
若当前结点stauis为2,且栈为空,则遍历结朿。
若指针指向了左子,则将左子作为当前结点,判断其左右子情况,按上述方法处理,直至遍历结朿。
三、源代码
下而给出的是用递归算法实现的程序的源代码:
#include
#include
〃用递归的方式適历二叉树typedefstructnode
{intdata;
structnode*lChild,*rChild;
}Node;
inti=-l;
Node*buildTree(int*b)
{
Node*p;
if(b[++i]==0)p=NULL;
else
{p=(Node*)malloc(sizeof(Node));p->data=b(i];p->lChild=buildTree(b);p->rChild=buildTree(b);
}
returnp;
〃泄义二叉树的结点
〃结点的数据
〃结点左右子
〃控制下而函数中循环的
〃产生二叉树(利用先序递归产生)
//创建一个根结点指针
//如果传入的当前值为0则设其为空结点
〃开辟内存
〃设苣当前结点的数据
〃左子结点
〃右子
〃把创建的树的根肖点返回
if(root!
=0)
{
printf(N%d*\root->data);preOrder(root->lChild);preOrder(root->rChild);
}
}
voidinOrder(Node*root)
{
if(root!
=0)
I
inOrder(root->IChild);
printf(n%d*\root->data);inOrder(root->rChild);
}
}
voidpostOrder(Node*root)
{
if(root!
=0)
(
postOrdcr(root->IChild);postOrdcr(root->rChild);printf(N%d*\root->data);
〃如果根右点不为0
//打印当前结点
〃指向左子
〃指向右子
〃中序遍历
〃如果根肖点不为0
〃指向左子
〃打印当前结点
〃指向右子
〃指向左子
〃指向右子
〃打印当前结点
voidmain()
{
〃按先序次序输入树的结点(非0整数)来创建一个树空结点用0表示inta[]={l,2,4Q7,0Q0.3,5Q068Q0・9Q0};
int*b=a;
〃将指向数组首地址的指针传给bulidTree函数来创建树
Node*root=buildTree(b);printf("用递归方法\n\n前序遍历:
”);preOrder(root);
printf(H\ii中序遍历:
”);inOrder(root);
printf("\n后序遍历:
");postOrder(root);
getch();
〃打印提示内容
〃调用前序遍历函数
//打印提示内容
//调用中序遍历函数
//打印提示内容
〃调用后序遍历函数
下而给岀的是用非递归算法实现的程序的源代码:
#includc
#include
s->bottom=(Node*)malloc(100*sizeof(Node));
//用非递归的方式遍历二叉树
typedefstructnode
{intdata;
structnode*lChild,*rChild;
}Node;
typedefstruct
{
Node*bottom;
Node*top;
}Stack;
voidinit(Stack*s)
s->top=s->bottom;
}
intisEmpty(Stacks)
{
if(s.top==s.bottom)
return1;
else
return0;
}
voidpush(Stack*s,Nodenode)
{
*(s->top++)=node:
}
Nodepop(Stack*s)
{
Nodenode;
node=*(-(s->top));returnnode;
}
Nodepeek(Stack*s)
{
return*(s->top-l);
}
typedefstruct
{
Node*bottom;
Node*top;
JMyStack;
〃定义二叉树的结点
〃结点的数据
〃结点左右子
//创建栈
〃栈底指针
〃栈顶指针
〃初始化栈
〃为指针开辟内存
〃栈顶指针指向栈底指针
〃判断栈是否为空的函数
〃栈空返回1
〃不为空返回0
〃栈的push方法
〃给栈顶赋值然后top+1
〃出栈函数
〃声明一Node类型遍量
//node为栈顶元素然后top-1
〃返回pop出的结点
〃看栈顶元素
//返回栈顶元素
//创建栈(MyStack)结构体
〃栈底指针
〃栈顶指针
voidinitl(MyStack*s)
{
s->bottom=(Node*)malloc(100*sizeof(Node));
s->top=s->bottom;
}
voidpushl(MyStack*s,Nodenode)
{
*(s->top++)=node;
}
Nodepopl(MyStack*s)
{
Nodenode;
node=*(-(s->top));
returnnode;
}
Nodepeekl(MyStack*s)
{
return*(s->top-l);
}
intisEmptyl(MyStacks)
{
if(s.top==s.bottom)return1;
else
return0;
}
inttemp=-l;
Node*buildTrec(int*b)
{
Node*p;
if(b[++temp]==0)p=NULL:
else
{p=(Node*)malloc⑸zcof(Nodc));
p->data=b[temp];p->lChild=buildTree(b);p->rChild=buildTree(b);
};
returnp;
}
voidpreOrder(Nodc*root)
{
Stackpo;
Nodecurr=*root;
〃初始化栈
〃开辟内存
〃栈顶指针指向栈底指针
〃进栈方法
〃给栈顶賦值然后top+1
〃出栈函数
〃声明一Node类型遍量
//node为栈顶元素然后top-1
〃返回pop出的结点
〃查栈顶元素
//返回栈顶元素
〃判断栈是否为空
//栈空了返回1
〃不为空返回0
〃产生二叉树
//创建一个根结点指针
//如果传入的当前值为0则设其为空结点
〃开辟内存
〃设置当前结点的数据
〃左子结点
〃右子
〃把创建的树的根结点返回
//前序遍历
〃声明一个栈
〃当前结点为根结点
〃初始化找
〃当前结点不为空且栈不为空
〃如果当前结点为空
〃当前结点指向pop出栈的结点
〃如果右子为空
〃将右子进栈
//打印当前结点的内容
〃如果左子不为空
〃当前子指向左子
〃当前子指向pop出栈结点
〃如果左子右子都为空
〃打印当前结点的内容
〃当前结点置空
〃中序遍历
〃声明一个栈
〃当前结点指向根结点
1:
当前结点指向了左结点
init(&ms);
while(curr.data!
=OllisEmpty(ms))
{
if(curr.lChild!
=NULL&&flag==O){
push(&ms,curr);curr=*curr.lChild;
}else
{
printf("%d”,curr.data);if(curr.rChild!
=NULL)
〃初始化栈
//当前结点不为空且栈不为空
〃左子不为空且没去过左子
〃当前子进栈
〃当前结点指向左子
〃打印当前结点的内容
〃左子为空
init(&po);“h^
while(curr.data!
=OII!
isEmpty(po)){
if(curr.data==O)
(
curr=pop(&po);
}if(cun\rChild!
=NULL)
(
push(&po,*cun\rChild);}
printf(H%d'\curr.data);if(cun\IChild!
=NULL){
curr=*curr.lChild;
}else
(
curr=pop(&po);
}
if((curr.lChild==NULL)&&(curr.rChild==NULL))(
printf("%d”,curr.data);curr.data=O;
}
}
}
voidinOrder(Node*root)
{
Stackms;
Nodecurr=*root;
intflag=0;
//设置一个标志0:
当前结点指向了右结点
}
flag=O;
}
if(cun\rChild==NULL&&ciHT.lChild=NULL)
//flag置0
〃如果左右子都为空
printf(H%d^.curr.data);if(isEmpty(ms)=l)break;curr=pop(&ms);
flag=l;
}
}
//打印当前结点的内容
//栈空则结束循环
〃当前子指向pop出栈的结点
//flag置1
}
voidpostOrder(Node*root)〃后序遍历
{
〃声明左右栈如果当前结点有左子则进左栈若没左子但是有右子则进右栈
Stackmsl:
〃声明左栈
〃声明右栈
〃结点指向树的根结点
MyStackmsr;Nodecurr=*root;
intflag=O;
//设置一个标志0:
进左栈1:
进右栈
//设置一个标志0:
没去过左右子树1:
去过左子树2:
去过右子树(两子树都去过)
〃初始化左栈
〃初始化右栈
intstatus=O;inil(&msl);init(&msr);
while(curr.data!
=OllisEmpty(msl)!
=OllisEmptyl(msr)!
=O)
//当前结点不为空且左右栈都不为空
{
if(status==O&&curr.lChild!
=NULL)
push(&msi,curr);
curr=*curr.lChild;
flag=0;
}elseif(status!
=2&&curr.rChild!
=NULL)(
push1(&msrxurr);
curr=*curr.rChild;
flag=l;
status=O;
}else
〃没去过左右子树且右子不为空
〃当前子进左栈
〃当前子指向左子
//flag置0
//没去过右子树且右子不为空
//当前子进右栈
〃当前子指向右子
//flag置1
//status置0
printf(H%d*\curr.data);
〃打印当前结点内容
〃如果当前子为空{〃如果flag标志为0
〃如果左栈不为空
〃指向左栈弹出的元素//status标志置为1
//指向右栈弹出的元素
//status标志置为2
〃如果右栈为空
〃指向右栈弹出的元素
〃指向左栈弹岀的元素//status标志置为1
〃若当前结点为空,结朿循环
}
if(curr.data==O)
if(flag==O)
{
if(isEmpty(msl)==O)
{
curr=pop(&msl);status=l;
}elseif(isEmpty1(msr)=O){
curr=pop1(&msr);status=2;
}
Jelse
{
if(isEmpty1(msr)==0)
{
curr=popl(&msr);status=2;
}elseif(isEmpty(msl)==O)
(
curr=pop(&msl);status=l;
}
}
if(curr.data==O)break;
}
)
}
voidmain()
{
intTree[]={124Q7・0.0・03,5Q06&0・0・9Q0};
int*tree=Tree:
Node*root=buildTree(tree);printf("用非递归方法\n前序遍历:
");preOrder(root);
printf(H\n中序遍历:
”);inOrder(root);
printf("\n后序遍历:
”);postOrdcr(root);
getch();
〃创建一个结点指向创建的树的根结点//打印提示内容
〃调用前序遍历函数
//打印提示内容
〃调用中序遍历函数
//打印提示内容
〃调用后序遍历函数
四、运行结果
D:
\Users\y3cg\Dblaop\新逹文舛夫\6bug\新建文本文栏・exe
用递归方搖
前序遍历:
124783569
目1
中序遍历:
274815369
后序遍历:
704259631.
a
kJ
图5递归算法运行结果图
图6非递归算法运行结果图
五、遇到的问题及解决
这部分我主要遇到了如下两个问题,其内容与解决方法如下所列:
•二叉树的建立
在刚开始进行时,如何建立二叉树难住了我,上课时听老师讲的是java的,在建立二叉树时和C语言不一样,在网上査阅了一部分资料后,找到了合适的办法,就是用结构体来储存二叉树,结构体内左义了二叉树的值和左右子,用0代表null,这样就可以用先序算法迪历输入了
•在进行非递归遍历时,编译不报错,但运行岀现下面的错误
到网上查询后,得知出现“AccessViolation”错误,有两种可能,第一种:
出现这个错误是因为试图访问一块已经被释放的内存,第二种是想使用一个还未创建对象的指针。
解决的办法是为指针分配内存,用(Nodc*)malloc(sizcof(Nodc))语句,这样就能解决这个问题。
六、心得体会
大一就开始学习C语言,可是,当时学的时候就觉得语言好像是学会了,可是一遇到编程的问题还是头大,总感觉没有什么思路,而这次作业,给自己一个不得不动手操作的机会,在十一的这几天中,复习了以前学过的C的基本知识,然后一点一点的摸索,遇到了错误和同学一起讨论,有问题自己想办法解决,最后程序调试出来的时候,会觉得貞•的很高兴。
我知道,我们现在的水平还很差,要想学习好这门课,在以后就要多动手操作,书上的例题或算法,最好都自己编写程序实现,那样可以加深对算法的理解,也可以提高我们编程的水平。
同时,很多的东西,理解了,可是在实现的时候还是有很多的错误发生,在以后的练习和实践中,应该多动手,遇到问题多思考,即使方案不是最优的也要想办法自己解决,然后和好的方案进行比较,从中找岀自己的差距在哪里。