二叉树的非递归遍历
2015-04-26 14:02
281 查看
树的非递归遍历比递归遍历要复杂,用栈来保存节点。
typedef struct node{
Elemtype data;
struct node* lChild;
struct node* rChild;
}BTNode;
1.先序遍历非递归算法
思路:由先序遍历过程可知,先访问根节点,再访问左子树,最后访问右子树,因此,先将根节点进栈,在栈不空时循环:出栈p,访问*p节点,若右孩子不为空将该右孩子节点进栈,若左孩子不空,再将左孩子节点 进栈。
void PreOrder(BTNode* b){
BTNode* St[MaxSize],*p;
int top=-1;
if(b!=NULL){
top++; //根节点进栈
St[top]=b;
while(top>-1){ //栈不为空时循环
p=St[top]; //退栈并访问该节点
top--;
printf("%c",p->data);
if(p->rChild!=NULL){ //右孩子节点进栈
top++;
St[top]=p->rChild;
}
if(p->lChild){ //左孩子结点进栈
top++;
St[top]=p->lChild;
}
}
printf("\n");
}
}
2.中序遍历非递归算法
思路:用指针指向当前要处理的结点。先扫描(并非访问)根结点的所有左结点并将它们一一进栈,当无左结点时表示栈顶节点无左子树,然后出栈这个结点,并访问它,将p指向刚出栈结点的右孩子,对右孩子做同样的处理。需要注意的是,当结点*p的所有左下结点进栈后,这时的栈顶结点要么没有左子树,要么其左子树已经访问过,就可以访问这个栈顶结点。如此这样,直到栈空。
void InOrder(BTNode* b){
BTNode* St[MaxSize],*p;
int top=-1;
if(b!=NULL){
p=b;
while(top>-1||p!=NULL){ //处理*p结点的左子树
while(p!=NULL){ //扫描*p的所有左结点并进栈
top++;
St[top]=p;
p=p->lChild;
}
//执行到此处时,栈顶元素没有左孩子或者左子树均以被访问过
if(top>-1){
p=St[top]; //出栈*p结点
top--;
printf("%c",p->data); //访问之
p=p->rChild; //转向处理*p的右孩子结点
}
}
printf("\n")
}
}
3.后序遍历非递归算法
思路:采用一个栈保存需要返回的结点指针,先扫描根节点的所有左结点并一一进栈,出栈一个结点*b作为当前结点,然后扫描该结点的右子树。当一个结点的左右孩子均被访问后再访问该结点,直到栈空为止。难点是如何判断一个结点*b的右子树已经被访问过(实际上当*b结点的右孩子被访问过,那么其油子树就已经被访问过),为此用p保存刚刚访问过的结点,初值设为NULL,若b-rChild==p成立(在后序遍历中,*b的右孩子结点一定刚好在*b访问之前被访问),说明*b的左右孩子均已被访问,可以访问*b;
void PostOrder(BTNode* b){
BTNode* St[MaxSize];
BTNode* p;
int flag,top=-1;
if(b!=NULL)
do{
while{(b!=NULL) //将b的所有左结点入栈
{
top++;
St[top]=b;
b=b->lChild;
}
//执行到此处说明栈顶元素没有左孩子或者左子树已经被访问过
p=NULL; //p指向栈顶结点的前一个已访问的结点
flag=1; //表示*b的左孩子已经被访问过或者是空
while(top!=-1&&flag){
b=St[top]; //取出当前栈顶元素
if(b->rChild==p){
/* 若p=NULL表示b的右孩子不存在,而其左子树不在或已经被 访问,所以可以访问*b;若p不等于NULL,表示b的右子树已访问(原因是p指向b的右子树中刚访问过的结点,而*p是b的右子树中后序序列的最后一个结点),所以可以访问*b */
printf("%c",p->data); //访问*b结点
top--;
p=b; //p指向刚刚访问过的结点
}else{
b=b->rChild; //b指向右孩子结点
flag=0; //表示*b的左孩子尚未访问过
}
}
}while(top!=-1)
}
printf("\n");
}
}
typedef struct node{
Elemtype data;
struct node* lChild;
struct node* rChild;
}BTNode;
1.先序遍历非递归算法
思路:由先序遍历过程可知,先访问根节点,再访问左子树,最后访问右子树,因此,先将根节点进栈,在栈不空时循环:出栈p,访问*p节点,若右孩子不为空将该右孩子节点进栈,若左孩子不空,再将左孩子节点 进栈。
void PreOrder(BTNode* b){
BTNode* St[MaxSize],*p;
int top=-1;
if(b!=NULL){
top++; //根节点进栈
St[top]=b;
while(top>-1){ //栈不为空时循环
p=St[top]; //退栈并访问该节点
top--;
printf("%c",p->data);
if(p->rChild!=NULL){ //右孩子节点进栈
top++;
St[top]=p->rChild;
}
if(p->lChild){ //左孩子结点进栈
top++;
St[top]=p->lChild;
}
}
printf("\n");
}
}
2.中序遍历非递归算法
思路:用指针指向当前要处理的结点。先扫描(并非访问)根结点的所有左结点并将它们一一进栈,当无左结点时表示栈顶节点无左子树,然后出栈这个结点,并访问它,将p指向刚出栈结点的右孩子,对右孩子做同样的处理。需要注意的是,当结点*p的所有左下结点进栈后,这时的栈顶结点要么没有左子树,要么其左子树已经访问过,就可以访问这个栈顶结点。如此这样,直到栈空。
void InOrder(BTNode* b){
BTNode* St[MaxSize],*p;
int top=-1;
if(b!=NULL){
p=b;
while(top>-1||p!=NULL){ //处理*p结点的左子树
while(p!=NULL){ //扫描*p的所有左结点并进栈
top++;
St[top]=p;
p=p->lChild;
}
//执行到此处时,栈顶元素没有左孩子或者左子树均以被访问过
if(top>-1){
p=St[top]; //出栈*p结点
top--;
printf("%c",p->data); //访问之
p=p->rChild; //转向处理*p的右孩子结点
}
}
printf("\n")
}
}
3.后序遍历非递归算法
思路:采用一个栈保存需要返回的结点指针,先扫描根节点的所有左结点并一一进栈,出栈一个结点*b作为当前结点,然后扫描该结点的右子树。当一个结点的左右孩子均被访问后再访问该结点,直到栈空为止。难点是如何判断一个结点*b的右子树已经被访问过(实际上当*b结点的右孩子被访问过,那么其油子树就已经被访问过),为此用p保存刚刚访问过的结点,初值设为NULL,若b-rChild==p成立(在后序遍历中,*b的右孩子结点一定刚好在*b访问之前被访问),说明*b的左右孩子均已被访问,可以访问*b;
void PostOrder(BTNode* b){
BTNode* St[MaxSize];
BTNode* p;
int flag,top=-1;
if(b!=NULL)
do{
while{(b!=NULL) //将b的所有左结点入栈
{
top++;
St[top]=b;
b=b->lChild;
}
//执行到此处说明栈顶元素没有左孩子或者左子树已经被访问过
p=NULL; //p指向栈顶结点的前一个已访问的结点
flag=1; //表示*b的左孩子已经被访问过或者是空
while(top!=-1&&flag){
b=St[top]; //取出当前栈顶元素
if(b->rChild==p){
/* 若p=NULL表示b的右孩子不存在,而其左子树不在或已经被 访问,所以可以访问*b;若p不等于NULL,表示b的右子树已访问(原因是p指向b的右子树中刚访问过的结点,而*p是b的右子树中后序序列的最后一个结点),所以可以访问*b */
printf("%c",p->data); //访问*b结点
top--;
p=b; //p指向刚刚访问过的结点
}else{
b=b->rChild; //b指向右孩子结点
flag=0; //表示*b的左孩子尚未访问过
}
}
}while(top!=-1)
}
printf("\n");
}
}
相关文章推荐