遍历二叉树的方法很多,常见的有递归方式,层入栈方式,非递归循环方式(利用树节点本身指针域标记遍历路径)。
由于递归方式和入栈方式与树形和树的高度有关,对于紧凑的内存情况下,很难对栈的容量进行确定。
使用非递归循环方式,仅使用3个结点变量即可遍历整个树。(见严蔚敏《数据结构》P208)
假设t为当前结点双亲结点,p为当前结点,q为当前结点的左孩子或右孩子,tag表示当前结点访问了左孩子还是右孩子;当q不为空,t、p、q往后移动,t=p,p=q,q则需要根据tag的值移动到左、右孩子结点,并根据tag的值修改t结点本身左孩子或右孩子指针;根据tag值和结点指针域,沿路返回,当有tag为0时进入其右子树,直至返回根结点为止。
因此,假定树结点的结构如下:
typedef struct tree_node{
struct tree_node *lc;
struct tree_node *rc;
int data;
int tag; //在本结点结构下,tag指明遍历0:左子树还是1:右子树;在孩子兄弟表示法中,则指明遍历0:孩子还是1:兄弟
}treenode;
以下代码以前序遍历为例:
#include <stdio.h>
#include <stdlib.h>
struct tree_node;
typedef struct tree_node{
struct tree_node *lc;
struct tree_node *rc;
int data;
int tag; //在本结点结构下,tag指明遍历0:左子树还是1:右子树;在孩子兄弟表示法中,则指明遍历0:孩子还是1:兄弟
}treenode;
void novel_previsit(treenode *T){ //防止递归额外使用空间导致内存溢出
treenode *t, *p, *q;
int flag = 0;
//t:当前结点父结点;p:当前结点;q:左孩子或右孩子结点
if(T==NULL)
return;
t = NULL;
p = T;
//由根结点出发,到返回到根结点且根结点的tag=1时结束
q = p->lc;
p->tag = 0;
while(flag==0){
if(p->tag==0)
printf("%d ", p->data);
if(q!=NULL){
if(p==T){
if(p->tag==1)
p->rc = NULL;
else
p->lc = NULL;
}
else{
if(p->tag==1)
p->rc = t;
else
p->lc = t;
}
t = p;
p = q;
q = p->lc;
p->tag = 0;
}
else{
/*if(p==T&&p->tag==1){
flag=1;
break;
}*/
//沿路返回并恢复指针域
while(1){
//先判断是否满足循环要求
if(p==T&&p->tag==1){
flag=1;
break;
}
if(p->tag==0){
q = p->rc;
p->tag = 1;
break;
}
else if(p->tag==1){
//沿路倒退咯
q = p;
p = t;
if(p->tag==1){
t = t->rc;
p->rc = q; //记得恢复指针
}
else{
t = t->lc;
p->lc = q;
}
}
}
}
}
}
void pre_visit_tree(treenode *T){
if(T!=NULL){
printf("%d ", T->data);
pre_visit_tree(T->lc);
pre_visit_tree(T->rc);
}
else{
return;
}
}
void pre_create_tree(treenode **T){ //递归法
int datatemp;
fflush(stdin);
scanf("%d", &datatemp);
if(datatemp==-1){
*T=NULL;
}
else{
if((*T=(treenode*)malloc(sizeof(treenode)))==NULL){
exit(0);
}
else{
(*T)->data=datatemp;
(*T)->lc = (*T)->rc = NULL;
pre_create_tree(&(*T)->lc);
pre_create_tree(&(*T)->rc);
}
}
}
int main(void){
treenode *tree;
pre_create_tree(&tree);
pre_visit_tree(tree); printf("\n");
novel_previsit(tree); printf("\n");
system("pause");
return 0;
}