目录
前言
二叉树的遍历是指按某条搜索路径访问树中的每个结点并使得每个结点均只被访问一次。遍历一棵二叉树需要决定对根节点N、左子树L和右子树R的访问顺序。按照先遍历左子树后遍历右子树的原则,我们常见的遍历顺序分为了先序遍历(NLR)、中序遍历(LNR)、后序遍历(LRN)三种,这其中的“序”指的就是根节点N何时被访问。
1.先序遍历(PreOrder)
先序遍历的操作过程为:访问根节点→先序遍历左子树→先序遍历右子树
对应的递归算法如下:
void PreOrder(BiTree T)
{
if (T!=NULL)
{
visit(T);
PreOrder(T->left);
PreOrder(T->right);
}
}
非递归算法如下:
void PreOrder2(BiTree T)
{
InitStack(S);
BiTree p = T;
while (p||!IsEmpty(S))
{
if (p)
{
visit(p);
Push(p);
p = p->left;
}
else
{
Pop(p);
p = p->right;
}
}
}
2.中序遍历(InOrder)
中序遍历的操作过程为:中序遍历左子树→访问根节点→中序遍历右子树
对应的递归算法如下:
void InOrder(BiTree T)
{
if (T!=NULL)
{
InOrder(T->left);
visit(T);
InOrder(T->right);
}
}
非递归算法如下:
void InOrder2(BiTree T)
{
InitStack(S);
BiTree p = T;
while (p||!IsEmpty(S))
{
if (p)
{
Push(p);
p = p->left;
}
else
{
Pop(p);
visit(p);
p = p->right;
}
}
}
3.后序遍历(PostOrder)
后序遍历的操作过程为:后序遍历左子树→后序遍历右子树→访问根节点
对应的递归算法如下:
void PostOrder(BiTree T)
{
if (T!=NULL)
{
PostOrder(T->left);
PostOrder(T->right);
visit(T);
}
}
后序遍历的非递归算法实现较为复杂,可以看到前两个方法的非递归代码其实也是访问结点操作的位置有区别,但是对于后序遍历来说,我们在访问一个结点的时候,要保证其左右都已经被访问。这里提供两种方法,第一种我们设定一个额外的指针,它指向最近访问的一个结点,这样就能分辨出结点访问的顺序;第二种方法在结点里面增加一个标识域来记录结点是否已经被访问。
//第一种方法
void PostOrder2(BiTree T)
{
InitStack(S);
BiTree p = T;
BiTree r = NULL;
while (p||!IsEmpty(S))
{
if (p)
{
Push(p);
p = p->left;
}
else
{
p = GetTop(S);
if (p->right && p->right!=r) p = p->right; //如果存在右子树且未被访问
else
{
Pop(p);
visit(p);
r = p; //记录最近访问的结点
p = NULL;
}
}
}
}
//第二种方法
void PostOrder2(BiTree T)
{
InitStack(S);
BiTree p = T;
BiTree r = NULL;
while (p||!IsEmpty(S))
{
if (p)
{
Push(p);
p = p->left;
}
else
{
p = GetTop(S);
if (p->right && !p.flag) p = p->right; //如果存在右子树且未被访问
else
{
Pop(p);
visit(p);
p.flag = 1; //标识域,置1代表该节点已经访问了
p = NULL;
}
}
}
}
注意:在后序遍历的非递归算法中,每次访问一个结点就代表该结点的子树都已经遍历过了,所以要把遍历指针p设置为NULL。
以上三种遍历的递归方法只是访问根节点的顺序不同,它们的时间复杂度都是O(n),在递归过程中,递归工作栈的深度恰好为树的深度,在最坏的情况下(n个结点深度为n,二叉树每层就一个结点)空间复杂度为O(n)。
层次遍历
按下图所示的顺序访问二叉树的每个结点就是二叉树的层次遍历
层次遍历需要借助一个队列,先将根节点入队然后出队访问,若它有左子树则左子树结点入队,若它有右子树则右子树结点入队,然后依次出队,出队时访问然后重复上述过程即可:
void LevelOrder(BiTree T)
{
InitQueue(Q);
BiTree p;
enqueue(T);
while (!IsEmpty(Q))
{
p = dequeue(Q);
visit(p);
if (p->left) enqueue(p->left);
if (p->right) enqueue(p->right);
}
}