在《二叉树的结构特点和性质》一片文章中我们简单了解了树和二叉树的定义,以及二叉树的一些性质和结构特点,没有看的或者对二叉树不熟悉的同学可以先去看一下,文章链接https://blog.youkuaiyun.com/qq_34322188/article/details/82346068,必须先了解二叉树的结构,才会让你更容易的去学习怎么去遍历一棵二叉树。
存储结构
二叉树的存储结构有两种:顺序存储结构和链式存储结构,由于顺序存储结构存在的很大的局限性,所以我们以链式存储结构讲解为主。
如左面的图所示,data域用于存储对应的数据元素,lchild和rchild分别表示左指针和右指针域,分别用于存储左孩子和右孩子结点的位置,这种存储结构又称为二叉链表存储结构。下面给出结点类型的定义:
typedef struct BTNode
{
char data;
struct BTNode *lchild;
struct BTNode *rchild;
}BTNode;
图 1
下面对图1中的二叉树来理解一下先中后序的递归遍历,当你理解了之后就会发现遍历so easy
先序遍历(递归)
访问顺序:(1)访问根结点
(2)先序遍历左子树
(3)先序遍历右子树
描述:由上面访问顺序的三个步骤你也能看出递归的思想,第一步先访问根结点;第二步先序遍历左子树,这时你可以把左子树单独当做一棵树再执行上述的三个步骤,访问根结点,如果还有左子树,再访问左子树的根结点......这就是递归的体现;第三步先序遍历右子树,跟第二步思想一样先是访问根节点,有左子树先访问左子树,然后右子树,直到树被遍历完。
图1先序遍历的结果为:ABDECFG
对应的算法描述如下:
void preorder(BTNode *t)
{
if(t!=NULL)
{
Visit(t); // 假设Visit()已被定义,包含了对结点t的一系列操作。
preorder(t->lchild); // 先序遍历左子树
preorder(t->lchild); // 先序遍历右子树
}
}
中序遍历(递归)
访问顺序:(1)中序遍历左子树
(2)访问根结点
(3)中序遍历右子树
描述:由上面访问顺序的三个步骤你也能看出递归的思想,第一步中序遍历左子树,这时你可以把左子树单独当做一棵树再执行上述的三个步骤,如果还有左子树,先访问左子树,如果还有左子树......直到左子树的左孩子为空,第二步先访问根结点;第三步中序遍历右子树,跟第一步思想一样有左子树先访问左子树,然后根结点,最后右子树,直到树被遍历完。
图1中序遍历的结果为:DBEACFG
对应的算法描述如下:
void inorder(BTNode *t)
{
if(t!=NULL)
{
inorder(t->lchild); // 中序遍历左子树
Visit(t); // 假设Visit()已被定义,包含了对结点t的一系列操作。
inorder(t->lchild); // 中序遍历右子树
}
}
后序遍历(递归)
访问顺序:(1)后序遍历左子树
(2)后序遍历右子树
(3)访问根结点
描述:由上面访问顺序的三个步骤你也能看出递归的思想,第一步后序遍历左子树,这时你可以把左子树单独当做一棵树再执行上述的三个步骤,如果还有左子树,先访问左子树,如果还有左子树......直到左子树的左孩子或者右孩子为空,第二步后序遍历右子树,跟第一步思想一样有左子树先访问左子树,然后右子树,最后根结点;第三步先访问根结点,直到树被遍历完。
图1后序遍历的结果为:DEBFGCA
对应的算法描述如下:
void postorder(BTNode *t)
{
if(t!=NULL)
{
postorder(t->lchild); // 后序遍历左子树
postorder(t->lchild); // 后序遍历右子树
Visit(t); // 假设Visit()已被定义,包含了对结点t的一系列操作。
}
}
总结
针对上述的先中后序的三种递归遍历算法,我们观看一下代码实现:都是递归调用方法,唯一不同的是函数Visit()在遍历方法中的位置不同。
先序遍历:函数Visit()在递归调用最前执行,这样就能先访问根结点。
中序遍历:函数Visit()在递归调用中间执行,这样就能先访问左子树的结点然后访问根结点。
后序遍历:函数Visit()在递归调用最后执行,这样就能后访问根结点。