算法笔记之二叉树的多种遍历-递归算法和仿递归算法

算法笔记之二叉树的多种遍历

引言

基于二叉树的递归定义,二叉树是由三个基本单元组成的,即根节点,左右子树。若能有效遍历他们,就相当于遍历了整个二叉树。根据访问根节点的次序并限制左子树先于右子树出现,可得三种递归遍历二叉树的算法:先序遍历,中序遍历,后序遍历。
基于二叉树的结构特点,也可以按照先上后下,先左后右的方法遍历即:层次遍历。
考虑栈溢出问题,将递归算法优化,就有了二叉树的非递归算法。

二叉树的一般形式

写代码前,我们先确定我们真正理解了上面几种遍历的内涵。
不如做个测试,写下上面这棵二叉树的递归和层次遍历次序。

  • 先序遍历:FCADBEHGM
  • 中序遍历:ACBDFHEMG
  • 后续遍历:ABDCHMGEF
  • 层次遍历:FCEADHGBM

如果你的答案正确,那么恭喜,让我们继续。如果不对,那就返回,重新理解下算法概念,再继续下去。

注意:下面的二叉树算法都是在二叉链表上实现的

//二叉树的二叉链表表示
typedef struct BiTNode{
TElemType data;
struct BiTNode *lchild, *rchild;
}BiTNode, * BiTree;

先序遍历 中序遍历 后序遍历

先给出先序遍历在链表上实现的伪代码

Status PreOrderTraverse( BiTree T, Status ( * Visit)(TElemType e)){
//采用二叉链表存储结构,Visit是对数据元素操作的应用函数
//先序遍历二叉树T的基本算法,是对每个数据元素调用函数Visit
//最简单的Visit函数是输出访问函数
if (T){//首先判空根节点
if(Visit(T->data))//根节点存在,如算法所言,访问
if (PreOrderTraverse(T->lchild,Visit))//递归左子树成功则继续向下递归右子树
if(PreOrderTraverse(T->rchild,Visit)) return OK;//递归右子树成功则退栈返回OK给上层函数
return ERROR;//递归左子树或者右子树失败则向上一级退栈并把ERROR值返回给上层函数
}else return OK;//根节点不存在则直接返回
}//PreOrderTraverse

很简单的,我们可以相应地写出二叉树的中序遍历和后续遍历算法伪代码,在这里就不详细赘述了。

二叉树的非递归算法表示

仿照二叉树的递归算法中递归工作栈的工作状态,以中序遍历二叉树为例,可以发现,工作状态中的明显规律:

  • 工作记录中包含两项,一项是递归调用的语句编号,一项是指向根节点的指针。则当栈顶指向根节点的指针非空时,应遍历左子树,即指向左子树的根的指针进栈;
  • 若栈顶记录中的指针值为空值,则应当返回上一层
    -若这层是左子树层,则应当访问栈顶记录中记录的根结点的值
    -若这层是右子树层,则表明当前层的小二叉树全部遍历结束,应当继续退栈。这意味着遍历右子树的时候不需要再浪费时间保存当前层的根指针,可以直接修改栈顶记录中的指针。
    即先把根结点入栈,然后把左子树一直入栈。当左子树为空时,停止入栈,此时栈顶结点就是我们需要访问的结点,退栈空结点后,取栈顶结点P并访问。考虑到该结点可能有右子树,所以再次判断该结点是否的右子树是否为空,为空则下次需要访问的结点仍在栈点,所以将p赋值为空,进入旧循环。若不为空则将p赋值为其右子树的值,循环结束的条件为p不为空或者栈不为空。

Status InOrderTraverse(BiTree T, Status (*Visit)(TElemType e){
InitStack(S);Push(S,T);//根指针进栈
while(!StackEmpty(S){//栈顶非空则进行如下操作
while(GetTop(S,p)&&p)Push(S,p->lchild);//不停地压左子树的根指针进栈,指针后移,直到指针向左走到尽头
Pop(S,p);//左子树根指针全部进栈后,多压的空指针退栈。
if(!StackEmpty(S){//当栈顶非空
Pop(S,p);//可能是最小左子树根结点不为空,退栈,下步是访问退栈后栈顶记录中根节点的值;可能是根结点不为空,退栈,下步是判断这个结点是否有右子树访问;可能是右子树不为空,则循环判断其是否有左子树。
if(!Visit(p->data))return ERROR;//应用函数操作失败则报错返回
Push(S,p->rchild);//访问右子树层,将P入栈,当p为空或者栈为空时推出循环
}return OK;
}

Status InOrderTraverse(BiTree T, Status (* Visit )(TElemType e)){
InitStack(S);
p=T;
while(p||!StackEmpty(S){
if§{Push(S,h);p=p->lchild;}//根指针进栈,遍历左子树
else{//根指针退栈,访问根节点,遍历右子树
Pop(S,p);if(!Visit(p->data)return ERROR;
p=p->rchild;
}
}
return OK;
}

二叉树的层次遍历

最后一种逻辑上最简单的遍历就是这里所讲的层次遍历了,下面,对其进行分析。
显然,对二叉树层次遍历的实现需要借助一种先进先出的数据结构-队列。
有三种技巧:数组长度做隔离,使用分隔符和深度优先搜索。这些技巧将在下章继续探讨。
注:上述笔记参考数据结构清华大学参考书版本

猜你喜欢

转载自blog.csdn.net/Greek_1999/article/details/83017945
今日推荐