二叉树的先中后序递归和非递归遍历(数据结构作业)
一、设计思想
我创建二叉树是用的先序创建,其中用‘#’代表空节点。
1、递归先序遍历
(1)如果当前节点为空节点(用‘#’代表空节点),结束当前函数
(2)打印当前节点
(2)递归当前节点的左子树
(3)递归当前节点的右子树
2、递归中序遍历
(1)如果当前节点为空节点(用‘#’代表空节点),结束当前函数
(2)递归当前节点的左子树
(3)打印当前节点
(4)递归当前节点的右子树
3、递归后序遍历
(1)如果当前节点为空节点(用‘#’代表空节点),结束当前函数
(2)递归当前节点的左子树
(3)递归当前节点的右子树
(4)打印当前节点
4、非递归先序遍历
(1)进入循环,当前节点不为空时:
打印当前节点
当前节点入栈
取当前节点的左子节点为当前节点
(2)弹出栈顶节点,取栈顶节点的右子节点为当前节点
(3)当前节点不为空或节点栈不空时循环执行(1)(2)
5、非递归中序遍历
(1)进入循环,当前节点不为空时:
当前节点入栈
取当前节点的左子节点为当前节点
(2)弹出栈顶节点,打印栈顶节点,取栈顶节点的右子节点为当前节点
(3)当前节点不为空或节点栈不空时循环执行(1)(2)
6、非递归后序遍历
(1)进入循环,当前节点不为空时:
当前节点的访问次数‘visit’赋值0;
当前节点入栈
取当前节点的左子节点为当前节点
(2)弹出栈顶节点
(3)如果栈顶节点的访问次数为0,那么visit++,取该栈顶节点的右子节点为当前节点,栈顶节点重新入栈
(4)否则如果栈顶节点的访问次数为1,打印栈顶节点
(5)当前节点不为空或节点栈不空时循环执行(1)(2)(3)(4)
二、算法流程图
图1 递归先序遍历二叉树
图 2 递归中序遍历二叉树
图3 递归后序遍历二叉树
图4 非递归先序遍历二叉树
图5 非递归中序遍历二叉树
图6 非递归后序遍历二叉树
三、源代码
1、递归
void RCS_DLR(BitNode *T)
{ if(T->data=='#')//痛苦面具,千万别将等号写错,出大问题 return;printf("%c",T->data);RCS_DLR(T->lchild);RCS_DLR(T->rchild);
}void RCS_LDR(BitNode *T)
{//printf("%c",T->data);if(T->data=='#')//痛苦面具,千万别将等号写错,出大问题 return; RCS_LDR(T->lchild);printf("%c",T->data);RCS_LDR(T->rchild);}void RCS_LRD(BitNode *T)
{ if(T->data=='#')//痛苦面具,千万别将等号写错,出大问题 return; RCS_LRD(T->lchild); RCS_LRD(T->rchild);printf("%c",T->data);}
2、非递归
void DLR(BitNode *T)
{BitNode *t,*pNode;t=T;pNode=(BitNode*)malloc(sizeof(BitNode));InitBitNode(pNode); ParsentStack *p;p=(ParsentStack*)malloc(sizeof(ParsentStack)); InitStack(p);//以下为核心代码while(t->data!='#'||p->size!=0)//当前节点不为空节点,或者节点栈不空时,说明还没遍历完{ while(t->data!='#')//当前节点不是空节点,那么一直取左孩子{printf("%c",t->data);Push(p,t);t=t->lchild; } Pop(p,pNode); //此处需要一个新的节点指针来承接弹出的栈顶元素 t=pNode->rchild; } free(pNode);free(p);printf("\n");
}
void LDR(BitNode *T)
{ BitNode *t,*qNode;t=T;qNode=(BitNode*)malloc(sizeof(BitNode));InitBitNode(qNode); ParsentStack *p;//存经过却未遍历的节点 p=(ParsentStack*)malloc(sizeof(ParsentStack)); InitStack(p);//以下为核心代码while(t->data!='#'||p->size!=0){while(t->data!='#'){Push(p,t);t=t->lchild;}Pop(p,qNode);printf("%c",qNode->data);t=qNode->rchild; }free(p);free(qNode);printf("\n");
}
void LRD(BitNode *T)
{BitNode *t,*qNode;t=T;qNode=(BitNode*)malloc(sizeof(BitNode));InitBitNode(qNode); ParsentStack *p;//存经过却未遍历的节点 p=(ParsentStack*)malloc(sizeof(ParsentStack)); InitStack(p);//以下为核心代码while(t->data!='#'||p->size!=0){while(t->data!='#'){t->visit=0;Push(p,t);t=t->lchild;}Pop(p,qNode);if(qNode->visit==0)//第一次弹栈,是经由根节点到右孩子,故需要重新入栈{ t=qNode->rchild;qNode->visit++;Push(p,qNode);} else if(qNode->visit==1){printf("%c",qNode->data); } }free(p);free(qNode);printf("\n");
}
3、全部代码
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define NULL 0
#define ERROR 0
#define YES 1
#define NO 0
typedef char ElemType;int counter=0; typedef struct BitNode//定义二叉树节点
{char data;int visit;struct BitNode *lchild,*rchild;
} BitNode;
typedef struct ParsentStack//定义节点栈
{struct BitNode *top,*base;int size;
}ParsentStack;
void InitStack(ParsentStack *P)//初始化栈
{P->top=(BitNode*)malloc(sizeof(BitNode));P->base=P->top;P->size=0;
}
void Push(ParsentStack *P,BitNode *node)//入栈
{*(P->top)=*node;P->top++;P->size++;
}
ElemType Pop(ParsentStack *P,BitNode *node)//弹栈
{if(P->size==0)return ERROR;P->top--;*node=*(P->top);P->size--;
}
void InitBitNode(BitNode *T) //初始化节点
{T->data=NULL;T->lchild=(BitNode*)malloc(sizeof(BitNode));T->rchild=(BitNode*)malloc(sizeof(BitNode));
}
void CreateBitNode(BitNode *T)//递归创建树的节点
{char ch;ch=getchar(); if(ch=='#'){ InitBitNode(T);T->data=ch;//未取完时取得下一个字符 return;}else{ InitBitNode(T);;T->data=ch; //递归创建二叉树 CreateBitNode(T->lchild);CreateBitNode(T->rchild);}
}
/*void CreateBitTree(BitNode *T)//读取输入,创建树
{char *str;str=(char*)malloc(sizeof(char)); gets(str);CreateBitNode(T);
}*/
void DLR(BitNode *T)
{BitNode *t,*pNode;t=T;pNode=(BitNode*)malloc(sizeof(BitNode));InitBitNode(pNode); ParsentStack *p;p=(ParsentStack*)malloc(sizeof(ParsentStack)); InitStack(p);while(t->data!='#'||p->size!=0){ while(t->data!='#'){printf("%c",t->data);Push(p,t);t=t->lchild; } Pop(p,pNode); //此处需要一个新的节点指针来承接弹出的栈顶元素 t=pNode->rchild; } free(pNode);free(p);printf("\n");
}
void LDR(BitNode *T)
{ BitNode *t,*qNode;t=T;qNode=(BitNode*)malloc(sizeof(BitNode));InitBitNode(qNode); ParsentStack *p;//存经过却未遍历的节点 p=(ParsentStack*)malloc(sizeof(ParsentStack)); InitStack(p);while(t->data!='#'||p->size!=0){while(t->data!='#'){Push(p,t);t=t->lchild;}Pop(p,qNode);printf("%c",qNode->data);t=qNode->rchild; }free(p);free(qNode);printf("\n");
}
void LRD(BitNode *T)
{BitNode *t,*qNode;t=T;qNode=(BitNode*)malloc(sizeof(BitNode));InitBitNode(qNode); ParsentStack *p;//存经过却未遍历的节点 p=(ParsentStack*)malloc(sizeof(ParsentStack)); InitStack(p);while(t->data!='#'||p->size!=0){while(t->data!='#'){t->visit=0;Push(p,t);t=t->lchild;}Pop(p,qNode);if(qNode->visit==0){ t=qNode->rchild;qNode->visit++;Push(p,qNode);} else if(qNode->visit==1){printf("%c",qNode->data); } }free(p);free(qNode);printf("\n");
}
//递归先序遍历二叉树
void RCS_DLR(BitNode *T)
{ if(T->data=='#')//痛苦面具,千万别将等号写错,出大问题 return;printf("%c",T->data);RCS_DLR(T->lchild);RCS_DLR(T->rchild);
}void RCS_LDR(BitNode *T)
{//printf("%c",T->data);if(T->data=='#')//痛苦面具,千万别将等号写错,出大问题 return; RCS_LDR(T->lchild);printf("%c",T->data);RCS_LDR(T->rchild);}void RCS_LRD(BitNode *T)
{ if(T->data=='#')//痛苦面具,千万别将等号写错,出大问题 return; RCS_LRD(T->lchild); RCS_LRD(T->rchild);printf("%c",T->data);}
void main(int argc, char *argv[])
{printf("请输入要创建的二叉树节点:\n");BitNode *Tree=NULL;Tree=(BitNode*)malloc(sizeof(BitNode));CreateBitNode(Tree);printf("先序遍历:");RCS_DLR(Tree);printf("\n");printf("非递归先序遍历:");DLR(Tree);printf("中序遍历:");RCS_LDR(Tree);printf("\n"); printf("非递归中序遍历:");LDR(Tree);printf("后序遍历:");RCS_LRD(Tree); printf("\n"); printf("非递归后序遍历:");LRD(Tree);
}
四、运行结果
图7 遍历二叉树运行结果
五、遇到的问题及解决
这部分我遇到了如下两个问题,其内容与解决方法如下所列:
1、 描述:在非递归实现前序遍历二叉树时,使用节点栈储存经过但尚未遍历的节点,当节点弹出时,用当前节点的指针t直接作为Pop函数的参数承接栈顶节点会导致结果出错。
解决方法:在解决问题时,一开始没有找到原因,猜测是内存的问题,于是改写了初始化节点的函数,可是在改写之后也不能正确输出。于是上网查找资料,参考他人所写的非递归遍历二叉树中弹出栈顶节点这一步,发现正确的做法是需要一个临时的节点变量来接收弹出的栈顶元素,而不是直接用当前节点指针。猜测当前节点指针作为参数时,因为传过去的是节点指针,所以是实参,当弹出栈顶元素时,并非仅仅改变了当前节点,而是将二叉树的当前节点改变了,导致了输出结果的出错。当使用临时变量承接弹出栈顶节点后,问题得到解决。
2、 描述:遍历二叉树需要先创建二叉树,创建二叉树需要做到读取键盘的输入,一开始考虑一次性读取进来,然后每次取得字符数组的下一位,实现依次读取字符的效果。因为有三个递归算法,所以为了保证计数的稳定,我使用一个全局变量来表示当前取得的字符下标,每当创建完一个节点后该全局变量加1。但是这样做之后,输出会出错,并且即使是相同的输入也会有不同的输出。
解决办法:因为一直无法输出结果,所以我进行了单步执行调试,并监视相关变量。然后发现了一个奇怪的现象,当我使用单步执行执行该程序时,程序的输出结果就正确了。这让我想到了前段时间所学的“狼羊赛跑”程序,同样的输入却输出不同的结果,而且当单步执行时又能执行成功,所以很有可能是线程相关的问题,而且我也查到说全局变量是线程不安全的。但是可惜受限与知识水平未能解决该问题,于是我选择换成函数getchar,去掉全局变量,每次递归时重新调用getchar即可解决该问题。
六、心得体会
本次项目是一个百行级别代码程序,代码量不算很多,但是设计的算法算法思想却不少,在递归实现二叉树遍历中用到了递归和回溯的算法思想,而非递归实现,则考验了学生递归转非递归的能力。
刚着手时几乎是一点头绪都没有,后来反复观看了关于二叉树遍历的视频和博客,彻底熟悉了二叉树的前中后序遍历过程,先在纸上写出了大致思路,最后终于才搭出了大致的项目框架。在这个抽象问题模型并写出算法的过程中,我对二叉树的理解得到了加深,逻辑思维能力也得到了锻炼。
项目实现过程中找bug,改bug也是一大难题。这次项目中解决各种问题的经历锻炼了我排查解决问题的能力,为我以后的编程提供了经验。尤其是利用单步执行和监视变量来排查运行过程中出现的错误的过程,使我对递归算法的执行顺序有了更深的认识,正是因为对递归算法的流程的熟悉,才使用非递归的算法写出了遍历二叉树程序,可以说单步执行功不可没了。
二叉树的先中后序递归和非递归遍历(数据结构作业)相关推荐
- 数据结构之二叉树的前中后序遍历以及层序遍历
学习目标:读完这篇博客搞定二叉树的前中后序以及层序遍历 首先:你应该明白什么是二叉树,下面这幅图就是一个完全二叉树 其实所谓的二叉树就是一个节点有小于等于二个分支的树,可以没有分支,可以有1条分支,可 ...
- 数据结构与算法(java):树-二叉树(二叉查找树(BST)、线索化二叉树、哈夫曼树、平衡二叉树【AVL】、二叉树的前中后序遍历)
二叉树 1.定义 二叉树 就是度不超过2的树(每个结点最多只有两个子结点).如图 2.特殊二叉树 满二叉树 当二叉树的每一个层的结点树都达到最大值,则这个二叉树就是满二叉树. 完全二叉树 叶结点只能出 ...
- 二叉树的前,中,后序遍历(思路分析) [Java][数据结构]
二叉树的前,中,后序遍历(思路分析) 前序遍历: 先输出父节点, 再遍历左子树和右子树 中序遍历: 先遍历左子树, 再输出父节点,再遍历右子树 后序遍历: 先遍历左子树,再遍历右子树,最后输出父节点 ...
- 深入理解二叉树的前中后序
一.问题 二叉树的前中后序遍历到底是什么,仅仅是三个顺序不同的 List 吗?为什么多叉树没有中序遍历? 二.遍历框架 所谓二叉树遍历是按某种特定规则,依次对二叉树中的节点进行相应的操作,并且每个节点 ...
- Java二叉树的前中后序遍历
Java二叉树的前中后序遍历 1.前序遍历 1.1前序遍历概念 1.2前序遍历习题 2.中序遍历 2.1中序遍历概念 2.2中序遍历习题 3.后续遍历 3.1后序遍历概念 3.2后序遍历习题 大家好, ...
- 【Java数据结构】二叉树的前中后序遍历(递归和非递归)
二叉树的遍历 递归做法 前序遍历 中序遍历 后序遍历 非递归 前序遍历 中序遍历 后序遍历 二叉树遍历是二叉树的一种重要操作 必须要掌握 二叉树的遍历可以用递归和非递归两种做法来实现 递归做法 前序遍 ...
- 二叉树的前中后序遍历之迭代法(非统一风格迭代方式)
文章目录 前言 一.前序遍历(迭代法) 二.中序遍历(迭代法) 三.后序遍历(迭代法) 总结 前言 「递归的实现就是:每一次递归调用都会把函数的局部变量.参数值和返回地址等压入调用栈中」,然后递归返回 ...
- 二叉树的前中后序遍历(考试常考)
二叉树遍历的概念 二叉树的遍历是按某种规则对二叉树的每个节点均只被访问一次,根据根节点访问位置的不同分为三种:先序遍历(根左右).中序遍历(左根右).后序遍历(左右根). 由于树是通过 ...
- 图解二叉树的先中后序遍历
二叉树前中后序介绍 对于一个二叉树而言,前序(根左右),中序(左根右),后序(左右根) . 可以简单这样理解: 所有的顺序都是以根在根.左.右 三个元素中的位置来决定的.根在前面,就是前序(根左右), ...
最新文章
- 第二阶段第三次站立会议
- matlab与python通信_python和matlab之间数据传输方法
- 深入了解volatile
- Linux终端下 dstat 监控工具
- numpy实现神经网络代码(mnist手写体识别)
- python staticmothed classmethod
- iOS中都有什么设计模式?各个设计模式的作用 (转载)
- Python bytearray/bytes/string区别
- python定时执行脚本实例
- Windows同步对象Event和Linux的条件变量
- mysql sql 限制条数据类型_数据库的数据类型和约束条件
- c语言中什么函数可以作为虚函数,C++语言中的“虚函数”就像C语言中的指针,必须要弄懂的...
- 了解开源图表组件FusionChartsFree
- mysql查询成绩表中课程编号升序_数据库的常见题型--------------(查询)
- JavaSE基础案例之模拟斗地主
- 19年全国电赛 H题电磁炮硬件电路+可控硅
- 计算机重启后桌面文件全部丢失,蓝屏后桌面文件丢失,电脑蓝屏桌面文件丢失...
- Mysql日志时间与系统时间相差八小时
- windows下忘记mysql密码,跳过登录进入mysql解决方案
- html文字注释,css如何注释?
热门文章
- Linux测试主机之间连通性和端口是否开放的方法
- 2017年ACM第八届山东省赛I题: Parity check(判断 第n项斐波那契数列奇偶性)
- 使用Matlab将抖音视频转换成gif图片
- Android系统镜像编译、烧录及调试
- PPT中修改已插入对象的图标
- linux文件扫描并打印,Linux办公一条龙—Linux中扫描、打印的实现
- Android:自动点击屏幕
- 感恩陪伴,链接未来 | Conflux杭州应用开发运营中心成立
- 项目经理工具之Microsoft Project(MSP)
- Springcloudalibaba整合es!实现相关性排序,集成完代码真香