后序遍历二叉树是先访问左子树,再访问右子树,最后访问根结点。
算法思想:
- 先沿根结点,依次入栈,直到左孩子为空
- 读取栈顶元素;如果其右孩子不空且未被访问过,将右子树转执行 1;
- 否则,栈顶元素出栈并访问。
void PostOrder(BiTree T){
InitStack(S);
p=T;
r=NULL;
while(p!=NULL||!IsEmpty(s)){
if(p!=NULL){ //走到最左边
push(S,p);
p=p->lchild;
}
else{ //向右
GetTop(S,p); //读栈顶节点(非出栈)
if(p->rchild&&p->rchild!=r){ //若右子树存在,且未被访问过
p=p->rchild; //转向右
}
else{ //否则弹出结点并访问
pop(S,p);
visit(p->data); //访问该结点
r=p; //记录最近访问的结点
p=NULL; //结点访问完后,重置p指针
}
}
}
后序序列D E B C A
以此为例,按照代码推演一遍:
按照第一个if语句中的 p!=NULL 且 p=p->lchild;
依次将 ABD 入栈
此时,p指针是指向D结点的左子树,所以为空;
跳出第一个if(p!=NULL)判断
然后转到 GetTop(S,p); 语句
将p指针指向栈顶的D结点;
然而 if(p->rchild) 判断中,D也无右结点。
这时转到pop语句;将D结点作为后序遍历的第1个结点,且r指针指向D
因为p置为NULL了,所以跳转到GetTop(S,p);p指向了栈顶B结点.
可知p=p->rchild,把p指针指向了B的右结点E;(此时r指向D,p指向E)
因为p!=NULL,会push(S,p);
E结点入栈
此时堆栈情况:
因为E无左子树,同时E也无右子树,
if(p->rchild&&p->rchild!=r)
所以出栈访问,将E结点作为后序遍历的第2个结点;
r指向E结点,p置空
pop(S,p);
visit(p->data);
r=p;
p=NULL;
之后因为r指向E,p指向B (因为GetTop(S,p);
会把p指向B)
p->rchild!=r
会阻止再次访问 访问过的结点,即E结点,所以将B出栈,将E结点作为后序遍历的第3个结点;
r指向B,p置空
接着对栈顶元素A进行判断,C结点相应入栈出栈,同理,r指针指向C,防止二次访问右子树C;
最后将A出栈,栈空,break while循环。
r指针的作用:
可以看出p指针指向栈顶元素时,如果元素存在右子树,且r指针保留了访问过的右子树,就会阻止访问,直接使栈顶元素出栈
这也是后序遍历不同于先序、中序,所要特别处理的地方。