二叉树中序非递归遍历算法实现
大家好,我是逝去的粒子,从今天起,我将尝试着数据结构从0开始学习分享,此篇文章作为试验,一方面可以为自己做笔记防止遗忘,另一方面希望可以帮助大家。不废话,正式开始。
1.第一步,我们需要先序递归创建二叉树,栈,以及栈的基础方法,因为上述这些不是今天的重点且网上有很多讲解,我就不重复造轮子了,代码如下:
#include "stdio.h"
#include "stdlib.h"
#define MaxSize 50
typedef int ElemType;
//树的定义
typedef struct BiTNode{
int data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
//栈的定义
typedef struct{
BiTree data[MaxSize];
int top;
}SqStack;
//初始化
void InitStack(SqStack &s){
s.top=-1;
}
//判断栈空
bool StackEmpty(SqStack &s){
if(s.top==-1)
return true;
else
return false;
}
//进栈
bool Push(SqStack &s,BiTree x){
if(s.top==MaxSize-1){
return false;
}
s.data[++s.top]=x;
return true;
}
//出栈
BiTree Pop(SqStack &s,BiTree x){
if(s.top==-1){
return false;
}
x=s.data[s.top--];
return x;
}
//读取栈顶元素
bool GetTop(SqStack &s){
BiTree x;
if(s.top==-1){
return false;
}
x=s.data[s.top];
printf("%c",x->data);
return true;
}
//创建二叉树,递归创建,默认先序输入
void CreateBiTree(BiTree &T){
char c;
scanf("%c",&c);
if(c=='#'){
T=NULL;
}
else{
T=(BiTree)malloc(sizeof(BiTNode));
T->data=c;
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
}
}
2.第二步:假设我们先序遍历输入:12#46###3#5##,(#表示空节点),如下创建了一个这样的二叉树:
3.第三步:递归先序遍历,简单来说就是始终坚持先左后中再右,代码如下:
//中序遍历--递归
void InOrder(BiTree T){
if(T!=NULL){
InOrder(T->lchild);
printf("%c",T->data);
InOrder(T->rchild);
}
}
简单来说就是将树T的指针传过来,依次遍历,有的人传的是指针的地址,效果都一样
--------------------------------接下来就是今天的重点了:非递归中序-------------------------
4.第4步:非递归中序遍历算法思想-->借助栈,先有一个工作指针p,让其从头节点依次扫描左节点,并将扫描到的节点依次进栈,如果扫描的左节点为空了时,从栈中弹出一个节点,此时显然弹出的这个节点及其他的左子树都已经被访问过了,我们再使用p继续访问此节点的右节点,将其进栈,然后继续右孩子节点的所有左孩子,不停的迭代,直到所有节点全部访问过,并且栈为空。根据此思想,编写如下代码:
//中序遍历--非递归
void InOrder2(BiTree T){
SqStack s;
InitStack(s); //初始化栈
BiTree p=T,a; //遍历指针p
while(p||!StackEmpty(s)){
if(p){
Push(s,p);
p=p->lchild;
}
else{
GetTop(s); //在p出栈之前先查看
a=Pop(s,p); //出栈,a为接收出栈的p节点
p=p->rchild;
}
}
}
1.首先初始化栈s,我用的是顺序栈,有能力的同学可以尝试着用链栈。
2.创建工作指是针p和a,其中p指向树T,也就是p指向节点1。
3.进入循环(p不为空或者栈s不为空时)执行循环体:
做判断:如果工作指针p不为空,此时p指向的节点1进栈,
p继续指向左子树指向了节点2,
继续将2进栈,由于节点2没有左子树,所以进入else语句,
出栈之前输出节点值,那么此时输出的就是2,并将节点2弹出栈,
再寻找节点2的右节点,进入if语句,就这样不断迭代。
好了,现在我们看似代码还是比较ok的,因为我本身小白,没办法,此时,运行试试看。
果然没辜负我,又出错了,递归中序遍历能够正常实现,但非递归却只出现了一个数字2,再就没反应了,这是为什么。
5.第5步:调试阶段
为非递归这个函数添加一个断点,进行一步步调试,查看栈s以及p指针的变化。
直到有错误提示
此时我们发现了一个大问题,p指针的左右孩子都是为空,说明此时的p就是一个空指针,而且p的值为0x000000,有bug就解决,我发现,导致p错误的原因是当从栈中弹出一个节点时,我用的p指针已经不再是指向弹出那个节点的,而是指向了一个空节点,所以程序猜到else语句,对不对,那么原先写的p=p->lchild毫无疑问是错误的,此时,我想到用一个指针a来接收弹出的节点,然后将a赋给p,然后p代表当前弹出节点并继续访问其右节点。
如果有疑问的话,建议照着刚写的代码画着图实际演练一遍,或可以联系我。
//中序遍历--非递归
void InOrder2(BiTree T){
SqStack s;
InitStack(s); //初始化栈
BiTree p=T,a; //遍历指针p
while(p||!StackEmpty(s)){
if(p){
Push(s,p);
p=p->lchild;
}
else{
GetTop(s); //在p出栈之前先查看
a=Pop(s,p); //出栈,a为接收出栈的p节点
p=a;
p=p->rchild;
}
}
}
成功解决,当遇到bug时,要学会去调试代码,当然调试的前提是你必须要手动的会描述一个算法过程。今天的试验分享就到这,后面会不定期分享我会的数据结构,第一次写的不好请见谅。