二叉树的递归遍历很好写,也很好理解。但因为是递归程序,不可避免地需要调用系统栈,耗时较长,这里我们来探究一下二叉树的非递归遍历的算法。这种方法需要使用栈这种数据结构,这里关于栈的一些操作函数可以看成伪代码吧,先给出线序、中序、后序遍历的代码即说明。
先序遍历:
1 void PreOrderTraverse(BinTree b) 2 { 3 InitStack(S);///初始化创建栈 4 BinTree p=b;///p为工作指针 5 while(p||!isEmpty(s)) 6 { 7 while(p)///到最左下的孩子 8 { 9 printf(" %c ",p->date);///先序先遍历结点 10 Push(S,p);///入栈 11 p=p->lchild; 12 } 13 if(!isEmpty(s))///在栈不为空的情况下,左孩子为空,弹出该结点,遍历右孩子 14 { 15 p=Pop(s); 16 p=p->rchild; 17 } 18 } 19 }
再给出使用数组模拟的函数
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 void Preorder_n(BiTree bt) /*先序遍历的非递归算法*/ 2 { 3 BiTree stack[MAX],p; 4 int top=0,i; 5 for(i=0; i<MAX; i++) 6 { 7 stack[i]=NULL; /*初始化栈*/ 8 } 9 p=bt; 10 do 11 { 12 while(p) 13 { 14 printf("%c",p->data); 15 stack[top++]=p; 16 p=p->lchild; 17 } 18 if(top!=0) 19 { 20 p=stack[top-1];//去栈底 21 top--;//出栈 22 p=p->rchild; 23 } 24 }while(top!=0||p!=NULL); 25 }
中序遍历:
1 void InOrderTraverse(BinTree b) 2 { 3 InitStack(S);///初始化创建栈 4 BinTree p=b;///p为工作指针 5 while(p||!isEmpty(s)) 6 { 7 while(p) 8 { 9 Push(S,p);///中序现将结点进栈保存 10 p=p->lchild; 11 }///遍历到左下角尽头再出栈访问 12 p=Pop(s); 13 printf(" %c ",p->data); 14 p=p->rchild;///遍历右孩子 15 } 16 }
再给出使用数组模拟的函数
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 void InOrder_n(BiTree bt) /*中序遍历的非递归算法*/ 2 { 3 BiTree stack[MAX],p; 4 int top=0,i; 5 for(i=0; i<MAX; i++) 6 { 7 stack[i]=NULL; /*初始化栈*/ 8 } 9 p=bt; 10 do 11 { 12 while(p)//顺着左链走到尽头 13 { 14 stack[top++]=p; 15 p=p->lchild; 16 } 17 if(top>0) 18 { 19 p=stack[top-1]; 20 printf("%c",stack[top-1]->data); 21 top--; 22 p=p->rchild; 23 } 24 }while(top!=0||p); 25 }
后序遍历:后序遍历较前两种遍历方法比较难实现,原因在于需要遍历完左子树,遍历完右子树,最后才去访问根节点。这样栈顶结点可能会从他的左子树返回,也有可能从他的右子树返回,需要区分这种情况,如果是第一次从左子树返回,那么还需要去遍历其右子树,如果是从右子树返回,那么直接返回该结点就可以了。这里使用辅助指针来区分来源。
void PostOrderTraverse(BinTree b) { InitStack(S);///初始化创建栈 BinTree p=b, r=NULL;///p为工作指针,辅助指针r while(p||!isEmpty(s)) { if(p)///从根节点到最左下角的左子树都入栈 { Push(S,p);///中序现将结点进栈保存 p=p->lchild; } else { GetTop(S,p);///取栈顶,注意!不是出栈! if(p->rchild&&p->rchild!=r)///1.右子树还没有访问并且右子树不空,第一次栈顶 { p=p->rchild;///进入右子树 } else///右子树已经访问或为空,接下来出栈访问结点,第二次栈顶 { p=Pop(s); printf(" %c ",p->data); r=p;///指向访问过的右子树结点 p=NULL;///使p为空继续访问栈顶 } } } }
数组模拟:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 void PostOrder_n(BiTree bt) 2 { 3 BiTree stack[MAX],p,r; 4 int top=0,i; 5 for(i=0; i<MAX; i++) 6 { 7 stack[i]=NULL; /*初始化栈*/ 8 } 9 p=bt, r=NULL;///p为工作指针,辅助指针r 10 do 11 { 12 if(p)///从根节点到最左下角的左子树都入栈 13 { 14 stack[top++]=p;///中序现将结点进栈保存 15 p=p->lchild; 16 } 17 else 18 { 19 p=stack[top-1];///取栈顶,注意!不是出栈! 20 if(p->rchild&&p->rchild!=r)///1.右子树还没有访问并且右子树不空,第一次栈顶 21 { 22 p=p->rchild;///进入右子树 23 } 24 else///右子树已经访问或为空,接下来出栈访问结点,第二次栈顶 25 { 26 top--; 27 printf("%c",p->data); 28 r=p;///指向访问过的右子树结点 29 p=NULL;///使p为空继续访问栈顶 30 } 31 } 32 } while(p||top!=0); 33 }
层次遍历:
从二叉树的第一层(根节点)开始,从上至下逐层遍历,在每一层中又按照从左到右的顺序对结点逐个遍历。我们可以看出如果某个结点比同一层的先遍历,其孩子也将比其同层的孩子结点先遍历,这种先进先出的方式,不就是队列这种数据结构吗?
1 void LevelOrder(BiTree b) 2 { 3 InitQueue(Q);///初始化建立队列 4 BinTree p; 5 EnQueue(Q,b);///根节点入队 6 while(!isEmpty(Q))///队列不空循环 7 { 8 DeQueue(Q,p);///队头元素出队 9 printf(" %c ",p->data); 10 ///左右孩子入队 11 if(p->lchild!=NULL) 12 { 13 EnQueue(Q,p->lchild); 14 } 15 if(p->rchild!=NULL) 16 { 17 EnQueue(Q,p->rchild); 18 } 19 } 20 }
使用C++ STL库 queue 的函数:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 void LevelOrder(BiTree p) 2 { 3 queue<BiTree>Q;//使用C++ STL库 4 Q.push(p);//根节点入队 5 while(!Q.empty())//队列不空循环 6 { 7 p=Q.front();//取对头 8 printf("%c",p->data);//左右孩子入队 9 if(p->lchild!=NULL) 10 { 11 Q.push(p->lchild); 12 } 13 if(p->rchild!=NULL) 14 { 15 Q.push(p->rchild); 16 } 17 Q.pop();//队头元素出队 18 } 19 printf("\n"); 20 }