二叉树是最基本的树形结构,也是我们编程过程中经常碰到的数据结构,在二叉树上所有可用的操作中,遍历是最常用的操作,所谓二叉树遍历(Binary Tree Traversal),就是遵从某种次序,遍访二叉树中的所有结点,使得每个结点被访问一次,而且只访问一次。这里,“访问”的意思就是对结点施行某些操作,例如查找具有某种属性值的结点,输出结点信息,修改结点的数据值等,但要求这种访问不破坏它原来的数据结构。
对线性结构而言,遍历是一种基本的操作,但二叉树是一种非线性结构,每个结点可能有不止一个直接后继,这样必须规定遍历的规则,按此规则遍历二叉树,最后得到二叉树结点的一个线性序列。
下面分别介绍二叉树遍历的递归算法和非递归算法的相关技术与实现代码。
二叉树遍历的递归算法
下面三个程序分别给出了前序、中序、后序遍历二叉树的递归实现。它们的差别在于语句void visit(BinTreeNode<T> * p)所处的位置不同。
二叉树的中序遍历算法
template <class T>
void BinaryTree<T>:: InOrder(BinTreeNode<T> *subTree, void (* visit)(BinTreeNode<T> *p)){
//递归函数:此算法按照中序次序遍历以subTree为根的子树。
if(subTree != NULL){
InOrder(subTree->leftChild, visit);
visit(subTree);
InOrder(subTree->rightChild, visit);
}
};
二叉树的前序遍历算法
template <class T>
void BinaryTree<T>:: PreOrder(BinTreeNode<T> *subTree, void (* visit)(BinTreeNode<T> *p)){
//递归函数:此算法按照前序次序遍历以subTree为根的子树。
if(subTree != NULL){
visit(subTree);
PreOrder(subTree->leftChild, visit);
PreOrder(subTree->rightChild, visit);
}
};
二叉树的后序遍历算法
template <class T>
void BinaryTree<T>:: PostOrder(BinTreeNode<T> *subTree, void (* visit)(BinTreeNode<T> *p)){
//递归函数:此算法按照后序次序遍历以subTree为根的子树。
if(subTree != NULL){
PostOrder(subTree->leftChild, visit);
PostOrder(subTree->rightChild, visit);
visit(subTree);
}
};
理解了上述的遍历方法后,我们再来看一个二叉树后序遍历的应用:计算二叉树的结点个数
为了计算二叉树的结点个数,可以遍历根结点的左子树和右子树,分别计算出左子树和右子树的结点个数,然后把访问根结点的语句改为相加语句:二叉树根结点的左子树结点个数加上右子树结点个数,再加上根结点个数,得到整个二叉树的结点个数。
template <class T>
int BinaryTree<T>::Size(BinTreeNode<T> *subTree)const{
//私有函数:计算以*subTree为根的二叉树的结点个数
if (subTree == NULL) return 0;//递归结束: 空树结点个数为0
else return 1+Size(subTree->leftChild)+Size(subTree->rightChild);
};
二叉树遍历的非递归算法
三种不同次序的遍历二叉树的递归算法的结构相似,只是访问根结点及遍历左子树、遍历右子树的先后次序不同而已。如果暂时把visit(subTree)(访问根结点)这个不涉及递归的语句抛开,则三个算法递归走过的路线是一样的。在递归执行过程中,前序遍历的情形是每进入一层递归调用时访问根结点,再依次向它的左子树、右子树递归调用。中序遍历的情形是在左子树递归调用退出时访问根结点,然后向它的右子树递归调用。
为了把一个递归过程改为非递归过程,一般需要利用一个工作栈,记录遍历时的回退路径。
1. 利用栈的前序遍历非递归算法
下图显示了利用栈实现前序遍历a给出的二叉树过程。每次访问一个结点后,在向左子树遍历下去之前,利用这个栈记录该结点的右子女(如果有的话)结点的地址,以便在左子树退回时可以直接从栈顶取得右子树的根结点,继续其右子树的前序遍历。
#include "stack.h"
template <class T>
void BinaryTree<T> :: PreOrder(void( *visit)(BinTreeNode<T> *p)){
stack<BinTreeNode<T>*> S;
BinTreeNode<T> *p=root;
S.Push(NULL);
while(p!= NULL){
visit(p);
if(p->rightChild != NULL) S.Push(p->rightChild);
if(p->leftChild != NULL) p=p->leftChild;
else S.Pop(p);
}
};
2. 利用栈的中序遍历非递归算法
#include "stack.h"
template <class T>
void BinaryTree<T> :: InOrder(void( *visit)(BinTreeNode<T> *p)){
stack<BinTreeNode<T>*> S;
BinTreeNode<T> *p=root;
do{
while (p!=NULL){
S.Push(p);
p=p->leftChild;
}
if(! S.IsEmpty()){
S.Pop(p);visit(p);
p=p->rightChild;
}
}while(p!= NULL || ! S.IsEmpty());
};