1、四种遍历概念
(1)先序遍历:先访问根节点,再访问左子树,最后访问右子树。
(2) 后序遍历:先左子树,再右子树,最后根节点。
(3)中序遍历:先左子树,再根节点,最后右子树。
(4)层序遍历:每一层从左到右访问每一个节点。
每一个子树遍历时依然按照此时的遍历顺序。
如下图:
先序遍历:FCADBEHGM
后序遍历:ABDCHMGEF
中序遍历:ACBDFHEMG
层序遍历:FCEADHGBM,层序遍历一般很少用。
2、实现
(1)递归实现,递归实现难理解但是很容易实现,先序遍历先操作根节点,之后递归先左后右。中序遍历先递归左子树,再操作根节点,再递归右子树。后序遍历先递归左子树再递归右子树之后再操作根节点。这样实现起来就很简单了。
//遍历实现先序
//根左右
void PreOrder(Tree T)
{
if(T)
{
printf("%d",T->Element);//输出根节点,可以是其他操作
PreOrder(T->Left);
PreOrder(T->Right);
}
}
//遍历实现中序
//左根右
void PreOrder(Tree T)
{
if(T)
{
PreOrder(T->Left);
printf("%d",T->Element);//输出根节点,可以是其他操作
PreOrder(T->Right);
}
}
//遍历实现后序
//左右根
void PreOrder(Tree T)
{
if(T)
{
PreOrder(T->Left);
PreOrder(T->Right);
printf("%d",T->Element);//输出根节点,可以是其他操作
}
}
(2)非递归实现,非递归实现比较麻烦但是好理解,麻烦的是需要用到栈。
1、先序:首先把根节点压入栈中,此时根节点作为栈顶元素弹出访问。将当前节点的右子树和左子树分别入栈,考虑栈是先入后出因此必须先右子树先入栈,左子树后入栈。重复上述步骤直到栈为空。
2、中序:中序遍历的非递归版本比前序稍微复杂一点,除了用到辅助栈之外,还需要一个指针 p 指向下一个待访问的节点。如果 p 非空,则将 p 入栈,p 指向 p 的左子树。如果 p 为空,说明此时左子树已经访问到尽头了,弹出当前栈顶元素,进行访问,并把 p 设置成 p 的右子树的左子树,即下一个待访问的节点。
3、后序:采用一个辅助栈和两个指针 p 和 r,p 代表下一个需要访问的节点,r 代表上一次需要访问的节点。如果 p 非空,则将 p 入栈,p 指向 p 的左子树。如果 p 为空,代表左子树到了尽头,此时判断栈顶元素,如果栈顶元素存在右子树且没有被访问过 (等于 r 代表被访问过),则右子树入栈,p 指向右子树的左子树,如果栈顶元素不存在或者已经被访问过,则弹出栈顶元素,访问,然后 p 置为 null,r 记录上一次访问的节点 p。