总结归纳
先序遍历:根左右;中序遍历:左根右;后序遍历:左右根。 若选取层次建树,则需要一个链式队列辅助实现 (规定,不必问为什么) 。若选取层次遍历,也需要一个链式队列辅助实现。 该辅助队列的具体操作流程(重点理解): 队列结点的数据域 p 存放的是树结点的地址 ( BiTNode *p 类型 ) ,头指针 phead 指向该队列头部,尾指针 ptail 指向该队列尾部,队列指针 pCur 始终指向当前操作位置,当前操作位置指的是:有新结点加入二叉树时,新结点作为队列指针 pCur 所指向的树结点(队列结点的数据域)的子孩子。此时,若 pCur 所指向树结点的左孩子为空,将新结点置为该树结点的左孩子,并通过尾插法加入队列,更新队尾指针;若此时右孩子为空,将新结点置为该树结点的右孩子,更新队尾指针,因为此时 pCur 指针所指向的树结点左右孩子皆满,使得 pCur 指针右移,下次新结点入队时,pCur 指针指向的树结点的左孩子就又为空。层次遍历的具体流程(重点理解): 首先使根结点入队,循环判断队列是否为空,若队列不为空,让队头出队,并使该队头结点指向的树结点的左右孩子入队(没有则不做处理),然后继续循环判断。根据队列先进先出的特性,每个头结点出队后,该头结点所指向的树结点的左右孩子立即入队,在逻辑上左右孩子紧邻头结点,符合层次遍历的要求。层次建树的难点在于,较为复杂且难以理解,最后再总结一下:定义树结点,定义链式队列结点,队列结点的数据域为树结点的地址(BiTNode *p 类型)。树结点入树时,首先将队列结点通过尾插法入队,并通过 pCur 指针指向的队列结点的数据域拿到对应树结点的地址,再对左右孩子进行判空,然后入树。 在该代码中,pCur 是每次建树时重新定义的,所以在建树最后,要释放并置为 NULL ,如果不这么做,可以将 pCur 置为全局变量。也可以将头尾指针定义在队列内部,可以使得建树函数 BuildBinaryTree() 少传些参数,总之方法很多,均能实现。 该代码中所有函数均位于一个文件,弊端就在于:对于 EnQueue( ) 、DeQueue( ) 等函数需要根据树结点的定义特殊处理,我使用的 VS Code,只能出此下策。更好的办法是,利用 VS Studio 引用头文件的功能,直接引用封装好的队列代码,只需修改 typedef 对 ElemType 的重命名即可代替所有的特殊处理。
代码实践
# include <iostream>
# define MaxSize 10
using namespace std;
typedef char ElemType;
struct BiTNode {
ElemType data;
BiTNode * lchild, * rchild;
} ;
struct LinkNode {
BiTNode * p;
LinkNode * pNext;
} ;
struct LinkQueue {
LinkNode * front, * rear;
} ;
typedef BiTNode * BiTree;
bool InitTree ( BiTree & T) {
T = NULL ;
return true ;
}
void BuildBinaryTree ( BiTree & T, LinkNode * & phead, LinkNode * & ptail,
ElemType val) {
BiTNode * pTreeNew =
( BiTNode * ) calloc ( 1 , sizeof ( BiTNode) ) ;
LinkNode * pQueueNew =
( LinkNode * ) calloc ( 1 , sizeof ( LinkNode) ) ;
LinkNode * pCur = phead;
pTreeNew-> data = val;
pQueueNew-> p = pTreeNew;
if ( T == NULL ) {
T = pTreeNew;
phead = pQueueNew;
ptail = pQueueNew;
} else {
ptail-> pNext = pQueueNew;
ptail = pQueueNew;
if ( pCur-> p-> lchild == NULL ) {
pCur-> p-> lchild = pTreeNew;
} else if ( pCur-> p-> rchild == NULL ) {
pCur-> p-> rchild = pTreeNew;
phead = pCur-> pNext;
free ( pCur) ;
pCur = NULL ;
}
}
}
void PreOrder ( BiTNode * p) {
if ( p != NULL ) {
putchar ( p-> data) ;
PreOrder ( p-> lchild) ;
PreOrder ( p-> rchild) ;
}
}
void InOrder ( BiTNode * p) {
if ( p != NULL ) {
InOrder ( p-> lchild) ;
putchar ( p-> data) ;
InOrder ( p-> rchild) ;
}
}
void PostOrder ( BiTNode * p) {
if ( p != NULL ) {
PostOrder ( p-> lchild) ;
PostOrder ( p-> rchild) ;
putchar ( p-> data) ;
}
}
bool InitQueue ( LinkQueue & Q) {
Q. front = Q. rear = new LinkNode;
Q. front-> pNext = NULL ;
return true ;
}
bool QueueEmpty ( LinkQueue & Q) {
if ( Q. front == Q. rear) {
return true ;
} else {
return false ;
}
}
bool EnQueue ( LinkQueue & Q, BiTNode * x) {
LinkNode * p = new LinkNode;
p-> p = x;
p-> pNext = NULL ;
Q. rear-> pNext = p;
Q. rear = p;
return true ;
}
bool DeQueue ( LinkQueue & Q, BiTNode * & x) {
if ( Q. rear == Q. front) {
return false ;
} else {
LinkNode * p = Q. front-> pNext;
x = p-> p;
Q. front-> pNext = p-> pNext;
if ( Q. rear == p) {
Q. front = Q. rear;
}
delete p;
return true ;
}
}
void LevelOrder ( BiTree T) {
LinkQueue Q;
InitQueue ( Q) ;
BiTNode * p;
EnQueue ( Q, T) ;
while ( ! QueueEmpty ( Q) ) {
DeQueue ( Q, p) ;
putchar ( p-> data) ;
if ( p-> lchild != NULL ) {
EnQueue ( Q, p-> lchild) ;
}
if ( p-> rchild != NULL ) {
EnQueue ( Q, p-> rchild) ;
}
}
}
int main ( ) {
ElemType val;
BiTree T;
InitTree ( T) ;
LinkNode * phead = NULL ;
LinkNode * ptail = NULL ;
while ( scanf ( "%c" , & val) != EOF ) {
if ( val == '\n' ) {
break ;
} else {
BuildBinaryTree ( T, phead, ptail, val) ;
}
}
cout << "-----前序遍历-----" << endl;
PreOrder ( T) ;
cout << endl;
cout << "-----中序遍历-----" << endl;
InOrder ( T) ;
cout << endl;
cout << "-----后序遍历-----" << endl;
PostOrder ( T) ;
cout << endl;
cout << "-----层序遍历-----" << endl;
LevelOrder ( T) ;
cout << endl;
return 0 ;
}