一、二叉树的遍历
二叉树遍历是按照一定的次序访问二叉树中的所有结点,并且每个结点仅被访问一次。
一颗二叉树是由三个部分构成(根结点、左子树和右子树)构成,因此在遍历二叉树时也可以从任何部分开始遍历,所以有3!种遍历方法。但是我们约定在遍历二叉树时,先遍历左子树再遍历右子树,则对于非空二叉树,可以得到三种递归遍历方法,而这三种方法我们统称为深度优先遍历法。而广度遍历法则是以另一个角度来遍历二叉树,通过根结点来引出左右子树,最终实现遍历。
二、深度优先遍历法(递归)
2.1 先序(先根)遍历法
先根遍历法顾名思义就是先遍历根节点,再按照约定往下去遍历左子树和右子树。其遍历过程如下:
- 先访问根节点;
- 在遍历左子树;
- 最后遍历右子树。
在遍历过程中依旧是采用分治算法的思想,将大问题分解为小问题,再使用递归遍历整棵树。如下图,从根节点A开始,依次到左子树B,再通过递归以B为根继续向下遍历,最后遍历右子树C。
最终遍历的整一个流程为:A->B->D->NULL->G->NULL->NULL->NULL->C->E->NULL->NULL->F->NULL->NULL
该算法程序如下(C语言):
void PreOrder(BTNode* root)
{
if(root !=NULL)
{
printf("%c ",root->data);//先访问根节点
PreOrder(root->left);//再遍历左子树
PreOrder(root->right);//最后遍历右子树
}
}
2.2 中序(中根)遍历法
中根遍历思路与先根遍历的思路大同小异,只不过根要放到第二个遍历。按照约定先遍历左子树,再遍历根,最后遍历右子树。
如下图,依然采用分治的思想将这棵树分解为若干二叉树,首先从A结点开始发现A并不是做左子树,则继续往下走,走到B时发现还可以继续分,直到走到D的左子树时发现不可再分,然后开始往回走。依次这样递归下去,就可以遍历整棵树。
最终遍历的整个流程为:NULL->D->NULL->G->NULL->B->NULL->A->NULL->E->NULL->C->NULL->F->NULL
该算法程序如下(C语言)
void InOrder(BTNode* root)
{
if(root != NULL)
{
InOrder(root->left);//遍历左子树
printf("%c ",root->data);//访问根结点
InOrder(root->right);//遍历右子树
}
}
2.3后序(后根)遍历法
后根遍历就是将根放到最后去访问,先遍历左子树和右子树。这种方法与上述两种方法相同,均是采用分治算法的思想来进行遍历。
如下图所示,从根结点A进入程序后,要一直向下走直到左/右子树不可再分,才会返回访问根结点。
最终遍历的整个流程为:NULL->NULL->NULL->G->D->NULL->B->NULL->NULL->E->NULL->NULL->F->C->A
该算法程序如下(C语言)
void PostOrder(BTNode* root)
{
if(root != NULL)
{
PostOrder(root->left);//遍历左子树
PostOrder(root->right);//遍历右子树
printf("%c ",root->data);//访问根结点
}
}
三、广度优先遍历法(非递归)
广度优先遍历法也称层次遍历法,这种遍历与深度优先遍历法存在一定的区别。首先,广度优先遍历法是一层一层的从左到右的访问所有结点,直到一层访问完之后,再向下访问;其次,广度优先遍历法没有用到递归这种思想,且在使用广度优先遍历法时需要使用队列这一数据结构。但二者均使用了分治算法的思想。
其核心思想就是采用了队列的先进先出这一优势,通过根节点出队,带左右子树入队,来实现遍历整棵树。如下图,首先将根结点A传入队列中,然后进行根结点A出队,左右子树B、C分别入队;紧接着继续以分治的思想,让左子树B出队,B的左右子树继续入队;C也如此。重复该操作,直到该队列为空则完成遍历
最终结果为:
A B C D E F G
核心算法代码如下
void LevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);//初始化一个队列
if(root != NULL)//判断是否为空树
{
QueuePush(&q,root);//将根节点入队
}
while(QueueEmpty != NULL)//判断队是否为空
{
BTNode* front = QueueFront(&q);//将队头指针指向根节点
QueuePop(&q); //将根结点出队
printf("%c ",front->data);
if(front->left)//若左子树不为空则入队
{
QueuePush(&q,front->left);
}
if(front->right)//判断右子树
{
QueuePush(&q,front->right);
}
}
printf("\n");
QueueDestory(&q);//销毁队列
}