前言
二叉树的遍历是按一定规律对二叉树的每个结点访问且仅访问一次。
二叉树的遍历顺序有以下三种,分别为:
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
二叉树的递归遍历比较简单,但是非递归遍历的难度就要上一个台阶了,尤其是后序遍历。
接下来我们就看他们的具体实现方法。
前序遍历
编程思想:借助栈实现,利用栈先入后出的特性
1.访问且打印当前节点
2.如果有右孩子,则右孩子进栈
3.如果有左孩子,直接打印;如果没有左孩子,栈顶元素出栈,直到栈为空。
代码实现:
void BinaryTreePrevOrderNonR(BTNode* root)
{
Stack st;
BTNode * cur = root;
StackInit(&st, 100);
while (cur)
{
putchar(cur->data);
if (cur->rchild)//如果有右孩子就进栈
{
StackPush(&st, cur->rchild);
}
if (cur->lchild)//如果有左孩子就直接输出左孩子
{
cur = cur->lchild;
}
else//如果没有左孩子,栈顶元素出栈
{
cur = StackTop(&st);
StackPop(&st);
}
}
StackDestory(&st);
}
前序遍历相对来说容易理解,代码也比较简短,接下来看难度稍微增加一点的中序遍历。
中序遍历
编程思想:借助栈的先入后出原理
1.从当前结点开始遍历其左子树,直到左子树为空,并让所有结点入栈
2.取出栈顶元素并打印,访问其右子树,如果有右子树,重复过程1
3.如果没有右子树,重复过程2
代码实现:
void BinaryTreeInOrderNonR(BTNode* root)
{
BTNode * cur = root;
Stack st;
StackInit(&st, 100);
while (cur || !StackIsEmpty(&st))
{
for (; cur; cur = cur->lchild)//将当前结点及他的左孩子们入栈
{
StackPush(&st, cur);
}
cur = StackTop(&st);
//1、如果右孩子为空,for循环不进,直接取栈顶
//2、如果右孩子不为空,那么这是一个没有左孩子的二叉树
//第一种情况为左子树访问完毕,第二种情况是左子树为空,无论是哪一种情况,当前结点都要打印
putchar(cur->data);
StackPop(&st);
cur = cur->rchild;
}
StackDestory(&st);
}
完成了中序遍历的过程,那么重点来了
后序遍历
编程思想:借助栈先入后出的原理。
首先设置一个标记为0,只有该标记为栈顶元素时被置位为1,
1.遍历当前节点的左子树,所有结点入栈,直到左子树为空,此时所有结点的标记均为0
2.访问栈顶元素,并将栈顶元素置位,访问其右子树,若该结点有右子树,重复过程1,若该结点没有右子树,进入过程3
3.取出栈顶元素,并检查其父亲结点,若标记已经置位为1,则一并打印,直到第一个标记为0的结点,重复过程2
代码实现:
void BinaryTreePostOrderNonR(BTNode* root)
{
char tag[64];
BTNode * cur = root;
Stack st;
StackInit(&st, 100);
do{
for (; cur; cur = cur->lchild)//类似中序
{
StackPush(&st, cur);
tag[st.size - 1] = 0;//重置左子树的访问标记(LT)
}
while (!StackIsEmpty(&st) && tag[st.size - 1])//前面的条件只在最后一次循环跳出的时候生效
{
cur = StackTop(&st);
putchar(cur->data);
StackPop(&st);
}
if (!StackIsEmpty(&st))
//此条件只在最后一次循环跳出的时候生效
//后面的条件分两个情况
//1、当此次的cur为空时,上面的for不进,此条件成立
//2、当cur不为空时,上面的for进,此条件不成立
//当检测到当前的LT被置位(情况1),那么打印当前节点后,再去直接检查上一个结点(父节点)是不是也要被打印了,直到第一个标记为0的结点
{
cur = StackTop(&st);
//1、如果上面的while进了,那么证明左子树访问完毕
//2、如果上面的while每进,那么证明没有左子树
tag[st.size - 1] = 1;
cur = cur->rchild;
//左子树访问完毕后,访问右子树
}
} while (!StackIsEmpty(&st));//由于后序遍历根结点是左后出栈的,所以在根结点出栈前,栈不可能为空,所以栈空为循环跳出条件
StackDestory(&st);
}
总结
二叉树的三种非递归遍历都完成了,前序遍历和中序遍历都比较容易理解,但是后序遍历的难度就大大提升了,但是多理解几遍,对照过程做几道例题就可以掌握了,代码只是作为参考,可能还有更好的方法,欢迎大家一起交流。