二叉树的非递归遍历(面试常考)
Posted 雨轩(小宇)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树的非递归遍历(面试常考)相关的知识,希望对你有一定的参考价值。
目录 | 目录 |
---|---|
顺序表 | 单链表(不带附加头结点) |
双链表(带附加头结点) | 栈(顺序表实现) |
队列(链式,不带附加头结点) | 二叉树 |
二叉树的非递归遍历 |
前言
通常,对于二叉树的实现,面试官并不会去问你递归怎么实现,看了我前面写的二叉树,你就知道递归写起来比较简单。
对于二叉树,面试官一般会问你非递归算法是怎么实现的,因为非递归算法需要用到栈、队列,所以问起来比较有意义,可以联系一系列知识。
ps:不知道栈、队列的同学先去看看我写的文章哦!
例子
为了使大家更加明白,这里树简化了许多结点,只有五个结点,ABCDE
前序:ABDCE(根左右)
中序:BDAEC(左根右)
后序:DBECA(左右根)
前序遍历(栈实现)
- 首先访问根结点,每次访问一个结点后,在向左子树继续遍历之前,先用栈记录该结点的右孩子,这样可以在左子树退回时,取得根结点的地址,在继续右子树的遍历
栈的源码在我的博客中有写过,找不到的朋友可以翻上面的目录
栈主要的变化:
void PreOrder(BinTree* T)
{
assert(T);// 断言检查是否为空树
Stack s;
StackInit(&s);
StackPush(&s, T);// 先访问根结点
while (!StackEmpty(&s)) // 栈非空才继续访问
{
T = StackTop(&s);//访问元素
StackPop(&s);// 访问了就删除元素
printf("%c ", T->data);// 打印访问的元素
if (T->rightChild != NULL) // 先将右孩子保存在栈中
StackPush(&s, T->rightChild);
if (T->leftChild != NULL)
StackPush(&s, T->leftChild);// 先访问左孩子
}
}
中序遍历(栈实现)
- 先访问中序下的第一个结点,从根结点开始沿leftChild 走到最左下角的结点,该结点的leftChild 为NULL,访问它之后,在遍历该结点的右子树,重复执行上面的过程,直到该子树遍历完
前序跟中序都是使用栈进行遍历的,栈的变化点是一样的
void InOrder(BinTree* T)
{
assert(T);
Stack s;
StackInit(&s);
while (T || !StackEmpty(&s)) // 结点非空或者栈非空才继续循环
{
while (T != NULL)
{
StackPush(&s, T); // 先将根结点到左边的左孩子集体入栈
T = T->leftChild; // 先将左孩子入栈
}
if (!StackEmpty(&s))
{
T = StackTop(&s);//访问左孩子
StackPop(&s);// 左孩子出栈
printf("%c ", T->data); // 左孩子和根访问完了,在将右入栈
T = T->rightChild;
}
}
}
后序遍历(栈实现)
- 后序相对于前序和中序来说比较麻烦一点,后序遍历访问顺序是:左右根,这也就意味着先要暂存根结点的地址,所以这里必须表明是在左子树(L)还是在右子树(R)中。
- 先用栈暂存根结点地址,在遍历左子树,此时根结点的标记 tag = L,当访问完左子树结点之后,还要去访问右子树,此时修改根结点的标记 tag = R,右子树访问完在访问根结点。
这里用到了其他结构体,与前序、中序有所不同,读者注意区分
void PostOrder(BinTree* T)
{
if (T != NULL)
{
Stack s;
StackInit(&s);
StkNode w;//标记结构体
do
{
while (T != NULL)
{
w.p = T;
w.tag = L;
StackPush(&s, w);
T = T->leftChild;//一直将左孩子入栈
}
bool flag = true; // 继续循环标记,用于 R
while (flag && !StackEmpty(&s))
{
w = StackTop(&s);
StackPop(&s);
T = w.p;
switch (w.tag) // 判断栈顶的 tag 标记
{
case L:
w.tag = R; // 修改根结点的标签,这样才能访问右孩子
StackPush(&s, w);
flag = false; // 修改循环标记
T = T->rightChild; // 遍历右孩子,入栈
break;
case R:
printf("%c ", T->data); // 左右孩子出栈
break;
default:
break;
}
}
} while (!StackEmpty(&s)); // 循环遍历
}
}
层次遍历(队列实现)
- 在访问二叉树的某一结点时,把下一层的结点记忆在队列中,每当访问一个结点时,将它的子女依次加到队列的队尾,然后再去访问队头的结点,依次循环
用队列实现的,写了博客的,在目录里面,不理解的结合看
void LevelOrder(BinTree* T)
{
Queue q;
QueueInit(&q);
if (T != NULL)
{
QueuePush(&q, T); // 根结点先入队访问
}
while (!QueueEmpty(&q))
{
T = QueueFront(&q);
QueuePop(&q); printf("%c", T->data); // 访问队头元素
if(T->leftChild)
QueuePush(&q, T->leftChild); // 记录左孩子
if (T->rightChild)
QueuePush(&q, T->rightChild); // 记录右孩子
}
}
ps:涉及到栈和队列的内容,需读者先掌握着两块的知识,才能更好理解二叉树的非递归遍历
制作不易,记得三连!!!
以上是关于二叉树的非递归遍历(面试常考)的主要内容,如果未能解决你的问题,请参考以下文章