本文在由层次遍历构造二叉树的基础上写的
目录
正式开始打代码前的一些小知识
putchar()函数
int putchar(int char)
把参数 char 指定的字符(一个无符号字符)写入到标准输出 stdout 中。
函数以无符号 char 强制转换为 int 的形式返回写入的字符,如果发生错误则返回 EOF。
非递归
三种遍历的非递归形式需要借助栈来实现。
代码部分
结构体
//二叉树的数据结构
typedef struct BiTNode{
BiElemType Bidata;
struct BiTNode* lchild;
struct BiTNode* rchild;
}BiTNode, *BiTree;
//栈的数据结构
typedef struct {
ElemType data[MaxSize];
int top;
}SqStack;
前序遍历(递归、非递归)
//前序遍历(递归实现):根左右
void PreOrder(BiTree p)
{
if (p != NULL)
{
putchar(p->Bidata);
PreOrder(p->lchild);
PreOrder(p->rchild);
}
}
//前序遍历(非递归实现)
void PreOrder_non_Recrusive(BiTree T)
{
BiTree p = T;//p用于指向当前遍历到的结点
SqStack s;//新建栈
InitStack(s);//初始化栈
while (p || !IsEmpty(s))
{
if (p)//如果p不为NULL,输出p的data,继续寻找p的左子树,直到找到最左下的结点
{
putchar(p->Bidata);
Push(s, p);
p = p->lchild;
}
else
{
Pop(s, p);
p = p->rchild;
}
}
}
中序遍历(递归、非递归)
//中序遍历(递归实现):左根右
void InOrder(BiTree p)
{
if (p != NULL)
{
InOrder(p->lchild);
putchar(p->Bidata);
InOrder(p->rchild);
}
}
//中序遍历(非递归实现)
void InOrder_non_Recrusive(BiTree T)
{
BiTree p = T;
SqStack s;
InitStack(s);
while (p || !IsEmpty(s))
{
if (p)
{
Push(s, p);
p = p->lchild;
}
else
{
Pop(s, p);
putchar(p->Bidata);
p = p->rchild;
}
}
}
后序遍历(递归、非递归)
//后序遍历(递归实现):左右根
void PostOrder(BiTree p)
{
if (p != NULL)
{
PostOrder(p->lchild);
PostOrder(p->rchild);
putchar(p->Bidata);
}
}
//后序遍历(非递归实现)
//由于后序遍历是左右根,需要记录上一个被访问结点才能先输出其右子树的数据,比前序和中序复杂一点
void PostOrder_non_Recrusive(BiTree T)
{
BiTree p = T;
BiTree pre = T;//pre用于记录上一个被访问(Pop弹出)的结点
SqStack s;
InitStack(s);
while (p || !IsEmpty(s))
{
if (p && pre != p->lchild && pre != p->rchild)
{
Push(s, p);
p = p->lchild;
}
else
{
if (p && pre != p->rchild)
{
p = p->rchild;
}
else
{
Pop(s, p);
//printf("%d", IsEmpty(s));
putchar(p->Bidata);
pre = p;
GetTop(s, p);
}
}
}
}
打代码过程遇到的困难
关于对头文件xxx.h(这里是function.h)的引用问题(很初级很基础的问题)
我现在有一个头文件和两个cpp文件,分别是
function.h
main.cpp
stack.cpp
写代码的时候,我把所有的函数都写进了function.h,认为这样可以让cpp文件更加简洁,如下
function.h
//函数定义与实现
//前序遍历(递归实现):根左右
void PreOrder(BiTree p)
{
......
}
//中序遍历(递归实现):左根右
void InOrder(BiTree p)
{
......
}
//栈
void InitStack(SqStack& S);
bool IsEmpty(SqStack S);
......
main.cpp
#include"function.h"
int main()
{
//如前序遍历,中序遍历等操作
......
}
stack.cpp
#include"function.h"
//一些栈相关函数实现
/初始化栈
void InitStack(SqStack& S)
{
......
}
//判断栈是否为空
bool IsEmpty(SqStack S)
{
......
}
......
然后出现了报错,是说void InOrder(BiTree p)等函数已经在main.obj中定义过,在stack.obj中出现了重复定义。
于是我了解了一下头文件与cpp文件的关系:
简单说来(是很朴素的理解方法,并不专业),在main.cpp文件中输入#include"function.h"以后,就相当于把function.h中的代码往main.cpp中复制了一份,而我在stack.cpp中同样写了#include"function.h",因此对于在function.h中定义并实现的void InOrder(BiTree p)等函数,分别在main.obj和stack.obj都实现了,造成重复。
而function.h中对栈相关函数void InitStack(SqStack& S)等仅定义并未实现,因此没有报错。
关于栈的GetTop()函数问题
之前学习后我写的GetTop()函数如下
bool GetTop(SqStack S, ElemType& e)
{
if (IsEmpty(S))
{
return false;
}
e = S.data[S.top];
return true;
}
而这在后序遍历(非递归)函数中出了问题。后序遍历代码中有这样一句
//s是非递归时用到的栈,p是当前栈顶元素
GetTop(s, p);
当栈空时GetTop()函数返回FALSE,但不会对e赋值,这就导致p一直等于最后一个出栈的元素,从而造成无限循环。改成以下即可
bool GetTop(SqStack S, ElemType& e)
{
if (IsEmpty(S))
{
e = NULL;
return false;
}
e = S.data[S.top];
return true;
}
我想了一下,其实本来就应该有e = NULL;这一句,但之前的代码并没有因此出现什么问题,所以一直没有注意过这一点。