算法思想:
先序:NLR的访问顺序,由于R节点,即右孩子的访问需要通过根节点的指针来实现,所以,需要设一个栈来存储根节点。具体看代码:
void preorder(btree t){
initstack(s);//初始化栈
btree p=t;
while(p||!empty(s)){
if(p){//如果该节点非空
visit(p);//访问
push(s,p);//入栈,栈中元素全部非空
p=p->lchild;//走到最左
}
else{//空节点
pop(s,p);//退栈,栈中元素全部非空
p=p->lchild;//转向右子树
}
}//while
}
中序遍历和先序遍历几乎一样:
void midorder(btree t){
initstack(s);
btree p=t;
while(p||!empty(s)){
if(p){
push(s,p);
p=p->lchild;
}
else{
pop(s,p);
visit(p);
p=p->rchild;
}
}
}
后序遍历:LRN顺序。L和R都需要通过N的左右指针来指向,所以L和R未访问完之前,N都要放在栈内。具体为,从L返回时,N不能退栈,从R返回或R为空时,N退栈并访问。而要判断是从L返回还是后者,需要用一个指针r记录最近访问的节点来判断是从L还是R返回。
void postorder(btree t){
initstack(s);
btree p=t,r=null;//r记录最近访问的节点,初始化为空
while(p||!empty(s)){
if(p){//走到最左
push(s,p);
p=p->lchild;
}
else{//p为空
gettop(s,p);//先获取栈顶,而不是退栈
if(p->rchild&&p->rchild!=r){//右孩子存在且未被访问过
//转向右子树并再走向左孩子
p=p->rchild;
push(s,p);
p=p->lchild;
}
else{//没有右孩子或者右孩子被访问过(从右边返回)
//退栈并访问该根节点
pop(s,p);
visit(p);
r=p;//用r记录最近访问的节点
p=null;//p置空,以防再次入栈
}//else
}//else
}//while
}
后序遍历的应用:找祖先,栈顶元素以下的元素全部为其祖先。具体为,把visit部分改为:依次输出栈内元素。
若找x、y的共同祖先,假设x在y的左边,则必然先遍历到x,此时先把栈内元素复制到另一个辅助栈(x的祖先),继续遍历到y时,依次退栈并和辅助栈中的元素比较,若遇到相等的则为共同祖先。
btree conancestor(btree t,btnode *x,btnode *y){
//查找x,y的共同祖先并返回
initstack(s);
initstack(s1);//辅助栈
btree p=t,r=null;
while(p||!empty(s)){
if(p){
push(s,p);
p=p->lchild;
}
else{
gettop(s,p);
if(p->rchild&&p->rchild!=r){
p=p->rchild;
push(s,p);
p=p->lchild;
}
else{
pop(s,p);
if(p==x) stack s1=s;//将s栈中元素复制到辅助栈s1
if(p==y){
i=s1.top;
while(p!=s1.data[i]&&i>-1)
i--;
if(p==s1.data[i] return p;//找到共同祖先,返回p
pop(s,p);//没有找到,继续退栈
}//if(p==y)
r=p;
p=null;
}//else
}//else
}//while
}