遍历二叉树的方法很多,常见的有递归方式,层入栈方式,非递归循环方式(利用树节点本身指针域标记遍历路径)。
由于递归方式和入栈方式与树形和树的高度有关,对于紧凑的内存情况下,很难对栈的容量进行确定。
使用非递归循环方式,仅使用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(" "); novel_previsit(tree); printf(" "); system("pause"); return 0; }