【版权声明:转载请保留出处:blog.csdn.net/algorithm_only。邮箱:liuy0711@foxmail.com】
遍历是二叉树各种操作的基础,上一节给出的遍历算法是递归实现的,本节给出二叉树遍历的非递归实现,非递归实现需要使用前面讲到的数据结构——栈、队列来作为辅助空间。
- 先序遍历
int preorder_traverse(bitree bt, int (*visit)(elemtype e))
{
sqstack s;
bitree p;
init_stack(&s);
p = bt;
while (p || !is_stack_empty(s)) {
while (p) { /* 左结点依次进栈,向左走到尽头 */
visit(p->data);
push_stack(&s, p);
p = p->lchild;
}
if (!is_stack_empty(s)) {
pop_stack(&s, &p); /* 出栈 */
p = p->rchild;
}
}
return OK;
}
根结点先进栈,访问结点值,左孩子依次进栈,直到最左的孩子进栈。然后栈顶指针出栈,退至上一层,然后访问右子树,右子树访问完成之后,退至上一层,直至栈空,节点访问完成。先序遍历访问结点值是在结点进栈时。理解此算法最好找一棵简单的树跟着程序走一遍,在纸上画出栈的进栈、出栈情况。
- 中序遍历
int inorder_traverse(bitree bt, int (*visit)(elemtype e))
{
sqstack s;
bitree p;
init_stack(&s);
p = bt;
while (p || !is_stack_empty(s)) {
while (p) {
push_stack(&s, p);
p = p->lchild;
}
if (!is_stack_empty(s)) {
pop_stack(&s, &p);
visit(p->data);
p = p->rchild;
}
}
return OK;
}
中序遍历和先序遍历类似,只是访问结点值是在出栈的时候,而先序遍历是在进栈的时候。
- 后序遍历
int postorder_traverse(bitree bt, int (*visit)(elemtype e))
{
sqstack s;
bitree p, q;
init_stack(&s);
p = bt;
q = NULL;
while (p || !is_stack_empty(s)) {
while (p) {
push_stack(&s, p);
p = p->lchild;
}
if (!is_stack_empty(s)) {
get_top(s, &p); /* 取栈顶元素 */
if (!p->rchild || p->rchild == q) { /* 如果p没有右孩子,或右孩子已经访问过 */
visit(p->data);
q = p;
p = NULL;
--s.top;/* 退栈 */
}
else
p = p->rchild;
}
}
return OK;
}
后序遍历和先序、中序类似,但要稍复杂一点,需要判断结点是否有右孩子和右孩子是否访问过。
- 层次遍历
int levelorder_traverse(bitree bt, int (*visit)(elemtype e))
{
sqqueue sq;
bitree cur;
init_queue(&sq);
if (bt) {
in_queue(&sq, bt);
while (!is_queue_empty(sq)) {
out_queue(&sq, &cur);
visit(cur->data);
if (cur->lchild)
in_queue(&sq, cur->lchild);
if (cur->rchild)
in_queue(&sq, cur->rchild);
}
}
return OK;
}
层次遍历算法比较好理解,使用队列作为辅助空间,根节点首先进队列,如果队列不为空的话,访问结点值再出队列,然后左孩子进队列(如果有的话),右孩子进队列(如果有的话)。
- 总结
遍历二叉树算法基本操作是访问结点,不论按哪一种次序进行遍历,对含n个结点的二叉树时间复杂度都为O(n)。