用递归非递归两种方法遍历二叉树.docx
- 文档编号:12396464
- 上传时间:2023-04-18
- 格式:DOCX
- 页数:23
- 大小:213.25KB
用递归非递归两种方法遍历二叉树.docx
《用递归非递归两种方法遍历二叉树.docx》由会员分享,可在线阅读,更多相关《用递归非递归两种方法遍历二叉树.docx(23页珍藏版)》请在冰豆网上搜索。
用递归非递归两种方法遍历二叉树
一、设计思想
二叉树的遍历分为三种方式,分别是先序遍历,中序遍历和后序遍历。
先序遍历实现的顺序是:
根左右,中序遍历实现的是:
左根右,后续遍历实现的是:
左右根。
根据不同的算法分,又分为递归遍历和非递归遍历。
递归算法:
1.先序遍历:
先序遍历就是首先判断根结点是否为空,为空则停止遍历,不为空则将左子作为新的根结点重新进行上述判断,左子遍历结束后,再将右子作为根结点判断,直至结束。
到达每一个结点时,打印该结点数据,即得先序遍历结果。
2.中序遍历:
中序遍历是首先判断该结点是否为空,为空则结束,不为空则将左子作为根结点再进行判断,打印左子,然后打印二叉树的根结点,最后再将右子作为参数进行判断,打印右子,直至结束。
3.后序遍历:
指针到达一个结点时,判断该结点是否为空,为空则停止遍历,不为空则将左子作为新的结点参数进行判断,打印左子。
左子判断完成后,将右子作为结点参数传入判断,打印右子。
左右子判断完成后打印根结点。
非递归算法:
1.先序遍历:
首先建立一个栈,当指针到达根结点时,打印根结点,判断根结点是否有左子和右子。
有左子和右子的话就打印左子同时将右子入栈,将左子作为新的根结点进行判断,方法同上。
若当前结点没有左子,则直接将右子打印,同时将右子作为新的根结点判断。
若当前结点没有右子,则打印左子,同时将左子作为新的根结点判断。
若当前结点既没有左子也没有右子,则当前结点为叶子结点,此时将从栈中出栈一个元素,作为当前的根结点,打印结点元素,同时将当前结点同样按上述方法判断,依次进行。
直至当前结点的左右子都为空,且栈为空时,遍历结束。
2.中序遍历:
首先建立一个栈,首先将指针指向根结点,将根结点入栈,然后将指针指向左子,左子作为新的结点,将新结点入栈,然后再将指针指向当前结点的左子,直至左子为空,则指针返回,出栈一个元素,作为当前结点,打印该结点,然后将指针指向当前结点右子,将右子作为新的结点,结点入栈,再次进行上面的判断,直至当前结点右子也为空,则再出栈一个元素作为当前结点,一直到结束,使得当前结点右子为空,且栈空,遍历结束。
3.后续遍历:
首先从根节点开始遍历,看根节点是否为空,如果根节点不为空,将根节点的元素压入栈中,继续遍历,如果根节点有左子,索引移动到左子,并以左子为根节点继续遍历。
如果左子不为空,则索引移动到左子并入栈;如果左子为空,索引移动到栈顶元素所在的节点,如果此节点的右子不为空并且右子没有被访问过,则索引移动到右子,否则访问栈顶元素并输出,并且定义此节点为被访问过得节点,栈顶元素出栈,树节点定义为NULL,继续遍历。
二、算法流程图
递归算法:
先序非递归遍历:
图2先序遍历的非递归算法流程图
中序非递归遍历:
存在
不存在
存在
不存在
不为空
为空
图3中序遍历的非递归算法流程图
后序非递归遍历:
存在
不存在
存在
不存在
为空
不为空
图4后序遍历的非递归算法流程图
三、源代码
前序非递归
#include
structelement//用结构体构建二叉树元素
{
structelement*lchild;
intdata;
structelement*rchild;
};
intmain()
{
inti=1;
structelement*stack[30];
structelementa,b,c,d,e;//构建二叉树
a.data=1;b.data=2;c.data=3;d.data=4;e.data=5;
a.lchild=&b;a.rchild=&c;
b.lchild=&d;b.rchild=&e;c.lchild=0;c.rchild=0;
d.lchild=0;d.rchild=0;e.lchild=0;e.rchild=0;
stack[i]=&a;
while(i!
=0)//用数组逐个处理二叉树的元素
{
while(stack[i]!
=0)//取左子树直至为空
{
printf("%d\n",(*(stack[i])).data);//访问元素的数据部分
i++;
stack[i]=(*(stack[i-1])).lchild;
}
i--;//去掉空元素
if(i!
=0)
{
stack[i]=(*(stack[i])).rchild;//取右子树
}
}
getch();
}
中序非递归
#include
structelement//用结构体构建二叉树元素
{
structelement*lchild;
intdata;
structelement*rchild;
};
intmain()
{
inti=1;
structelement*stack[30];
structelementa,b,c,d,e;//构建二叉树
a.data=1;b.data=2;c.data=3;d.data=4;e.data=5;
a.lchild=&b;a.rchild=&c;
b.lchild=&d;b.rchild=&e;c.lchild=0;c.rchild=0;
d.lchild=0;d.rchild=0;e.lchild=0;e.rchild=0;
stack[i]=&a;
while(i!
=0)//用数组逐个处理二叉树的元素
{
while(stack[i]!
=0)
{
i++;
stack[i]=(*(stack[i-1])).lchild;
}//取左子树直至为空
i--;//去掉空元素
if(i!
=0)
{
printf("%d\n",(*(stack[i])).data);//访问元素的数据部分
stack[i]=(*(stack[i])).rchild;//取右子树
}
}
getch();
}
后序非递归
#include
structelement//用结构体构建二叉树元素
{
structelement*lchild;
intdata;
structelement*rchild;
};
intmain()
{
inti=-1,j=-1;
inttag[20];
structelement*p;
structelementstack[30];
structelementa,b,c,d,e;//构建二叉树
a.data=1;b.data=2;c.data=3;d.data=4;e.data=5;
a.lchild=&b;a.rchild=&c;
b.lchild=&d;b.rchild=&e;c.lchild=0;c.rchild=0;
d.lchild=0;d.rchild=0;e.lchild=0;e.rchild=0;
p=&a;
while(p!
=0||j!
=-1)//元素非空或没有遍历完全部节点就不结束
{
while(p)//当p指向的节点存在时,将其压入栈中,把0压入标志栈中
{//并将p指向其左子树
stack[++i]=*p;
tag[++j]=0;
p=(*p).lchild;
}
if(j>-1)
{
if(tag[j]==1)//当有右子树访问标志时,访问当前节点的数据
{
printf("%d\n",stack[i].data);
i--;
j--;
}
else//没有右子树访问标志时,取得当前节点的的右子树
{//并将右子树访问标志置1
p=stack[i].rchild;
tag[j]=1;
}
}
}
getch();
}
前序递归
#include
structelement//用结构体构建二叉树的元素
{
structelement*lchild;
intdata;
structelement*rchild;
};
intmain()
{
structelementa,b,c,d,e;
intpreRev(structelemente);//声明处理二叉树元素的递归方函数
//构建二叉树
a.data=1;b.data=2;c.data=3;d.data=4;e.data=5;
a.lchild=&b;a.rchild=&c;
b.lchild=&d;b.rchild=&e;c.lchild=0;c.rchild=0;
d.lchild=0;d.rchild=0;e.lchild=0;e.rchild=0;
preRev(a);//调用递归函数对二叉树元素进行逐个处理
getch();
}
intpreRev(structelemente)//定义递归函数
{
printf("%d\n",e.data);
if(e.lchild)preRev(*e.lchild);
if(e.rchild)preRev(*e.rchild);
}
中序递归
#include
structelement//用结构体构建二叉树的元素
{
structelement*lchild;
intdata;
structelement*rchild;
};
intmain()
{
structelementa,b,c,d,e;
intpreRev(structelemente);//声明处理二叉树元素的递归方函数
//构建二叉树
a.data=1;b.data=2;c.data=3;d.data=4;e.data=5;
a.lchild=&b;a.rchild=&c;
b.lchild=&d;b.rchild=&e;c.lchild=0;c.rchild=0;
d.lchild=0;d.rchild=0;e.lchild=0;e.rchild=0;
preRev(a);//调用递归函数对二叉树元素进行逐个处理
getch();
}
intpreRev(structelemente)//定义递归函数
{
if(e.lchild)preRev(*e.lchild);
printf("%d\n",e.data);
if(e.rchild)preRev(*e.rchild);
}
后序递归
#include
structelement//用结构体构建二叉树的元素
{
structelement*lchild;
intdata;
structelement*rchild;
};
intmain()
{
structelementa,b,c,d,e;
intpreRev(structelemente);//声明处理二叉树元素的递归方函数
//构建二叉树
a.data=1;b.data=2;c.data=3;d.data=4;e.data=5;
a.lchild=&b;a.rchild=&c;
b.lchild=&d;b.rchild=&e;c.lchild=0;c.rchild=0;
d.lchild=0;d.rchild=0;e.lchild=0;e.rchild=0;
preRev(a);//调用递归函数对二叉树元素进行逐个处理
getch();
}
intpreRev(structelemente)//定义递归函数
{
if(e.lchild)preRev(*e.lchild);
if(e.rchild)preRev(*e.rchild);
printf("%d\n",e.data);
}
四、运行结果
前序递归:
图5前序递归运行结果
中序递归:
图6中序递归运行结果
后序递归:
图7后序递归运行结果
前序非递归:
图8前序非递归运行结果
中序非递归:
图9中序非递归运行结果
后序非递归:
图10后序非递归运行结果
五、遇到的问题及解决
这部分我主要遇到了如下两个问题,其内容与解决方法如下所列:
●在进行非递归遍历时,编译不报错,但运行出现下面的错误:
到网上查询后,得知出现“AccessViolation”错误,有两种可能,第一种:
出现这个错误是因为试图访问一块已经被释放的内存,第二种是想使用一个还未创建对象的指针。
解决的办法是为指针分配内存,用(Node*)malloc(sizeof(Node))语句,这样就能解决这个问题。
●二叉树的建立
在刚开始进行时,如何建立二叉树难住了我,在网上查阅了一部分资料后,找到了合适的办法,就是用结构体来储存二叉树,结构体内定义了二叉树的值和左右子,用0代表null,这样就可以用先序算法遍历输入了。
六、心得体会
通过这次作业真的受益匪浅,感触良多:
首先,要提高编程能力,必须多动手,多实践,而不是仅仅局限在书本上,更不能眼高手低。
眼高手低,懒得动手,这就犯了编程人员的大忌。
大一我们开始学习C语言,这是我们接触到的第一种编程语言,但是当时觉得这门课好难,没有太大的兴趣去学习,没有付诸行动,动手少,结果考试险过。
通过这次作业,我再次看了C语言课本,边看边写代码,理解快,印象深刻,思维也活跃许多,状态也好,真正的意识到,编程能力需要靠实践来提升。
当自己写出意想的程序后,真的有些成就感。
再者,在老师的指导和要求下,改掉了很多的编程坏习惯的同时也养成了良好的编程习惯,另一方面我们态度端正了很多,认真完成好每一项任务,这样无形中提高了对自己的要求,同时也增强了我们的动手能力和编程能力。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 递归 方法 遍历 二叉
![提示](https://static.bdocx.com/images/bang_tan.gif)