二叉树的遍历
本文将逐渐把四种遍历方法(前序、中序、后序、层序)的代码都补充上来,其中前三种将列出递归、迭代(用栈实现)两种写法;层序则只有迭代(用队列实现)写法。
对前序、中序、后序三者来说,递归写法基本一致,比较简单;而对于前两者的迭代写法,也相对简单,模板类似;后序遍历的迭代写法则稍为复杂,涉及到二次入栈的问题,需要仔细学习。
一、前序遍历
先放代码:
void PreOrderTraverse(BiTNode *T)
{
if(T == nullptr)
return;
printf("%c", T->data);
PreOrderTraverse(T->leftChild);
PreOrderTraverse(T->rightChild);
}
实现的遍历效果如下图所示:
前序遍历的效果:
从根节点开始,先挨个遍历左孩子,即图中①②③的顺序,每经过一个节点,就读取一个节点;
直到某个节点没有左孩子,就来到到这个节点的兄弟节点(在有兄弟节点的情况下。若没有,则退回到其父节点的兄弟节点。若还没有,则退回到父节点的父节点的兄弟节点……),然后把兄弟节点当作一棵新的二叉树的根,以同样的方法遍历;
兄弟节点遍历完后,退回到它的父节点的兄弟节点(在其父节点有兄弟节点的情况下。若没有,则退回到父节点的父节点的兄弟节点……),逐次递归,直到遍历完所有节点。
总之,在前序遍历中,父节点的兄弟节点非常重要,往往是退回的第一选择。
二、中序遍历:
代码:
递归方式:
void MidOrderTraverse(BiTNode *T)
{
if(T == nullptr)
return;
MidOrderTraverse(T->leftChild);
printf("%c",T->data);
MidOrderTraverse(T->rightChild);
}
迭代方式:(from 浙江大学《数据结构》P114)
解释:在按中序遍历二叉树时,遇到一个节点,就把它压栈,并去遍历它的左子树;当左子树遍历结束后,从栈顶弹出这个节点并访问它,然后按其右指针再去中序遍历该节点的右子树。
void InorderTraverse(BinTree BT)
{
BinTree T = BT;
stack<ElemType> stk;
while(!stk.empty() || T) //这个判断条件老折磨人了,|| T是不太容易想到的
{
while(T)
{
stk.push(T);
T = T->left;
}
T = stk.top();
stk.pop();
printf("%d\n", T->val);
T = T->right;
}
}
三、后序遍历
代码:
void BackOrderTraverse(BiTNode *T)
{
if(T == nullptr)
return;
MidOrderTraverse(T->leftChild);
MidOrderTraverse(T->rightChild);
printf("%c",T->data);
}
后序遍历的效果:
由代码可以看出,后序遍历是先遍历当前节点的左右孩子,再显示它自己。
总之,后序遍历的基本步骤可以认为是先左子树、后右子树,且先孩子、后父亲的顺序来的。
四、层序遍历
层序遍历是依靠栈来实现的,首先把根节点的压栈,然后弹出根节点,同时把根节点的左右孩子压栈;
然后弹出根节点的左后孩子节点,在弹出每个节点的同时,把它的孩子压栈……这样就实现了层序遍历。
如下图所示: