先序、中序、后序遍历的递归类似:
递归算法:
1 void PreOrder(BiTree *T)//先序遍历 2 { 3 if(T!=NULL) 4 { 5 printf("%c",T->data); 6 PreOrder(T->lchild); 7 PreOrder(T->rchild); 8 } 9 }
非递归算法:先序遍历是访问顺序为根左右。首先初始化一个栈,初始化栈,让二叉树一直从根结点开始一直访问左子树直到为NULL,同时也输出访问结点的数值,在
把该结点入栈,当左子树为NULL时,就指向栈顶结点的右孩子结点,栈顶出栈,如果右孩子结点为NULL则继续出栈,不为空,就继续进行之前的操作。
- 依次访问左子树且输出左子树的data域并入栈,直到左子树为NULL
- 左子树为NULL时,令取栈顶元素的右孩子,出栈并访问右子树。
- 若右子树为NULL则继续取栈顶元素的右孩子并出栈,
- 不为NULL时,则重新操作1。
- 结束操作为:当栈为空且访问的结点为NULL时。
void PreOrder1(BiTree *T)//先序遍历的非递归算法 { BiTree *st[MaxSize]; int top=-1; while(T!=NULL||top!=-1) { if(T!=NULL) { Visit(T); st[++top]=T; T=T->lchild; } else { T=st[top--]; T=T->rchild; } } }
中序遍历的非递归与先序类书:
后序遍历的非递归算法:后序遍历的访问顺序为左右根。关键在于要有关键值来判断该结点的左右子树是否访问过了,只有当访问过后才能输出该结点。所以可以在栈中设置一个flag域作为关键值。
- 当左子树不为NULL时,依次访问左子树且入栈,且令该结点在栈中的flag域表示为访问过左孩子了。
- 左子树为NULL时,取栈顶结点,当该结点的右孩子为NULL时,将该结点出栈并输出该结点的值,继续取栈顶结点。若该结点的右孩子不为NULL,则将该结点在栈中的flag域设置为访问过右孩子了,然后在指向孩子的右结点,重复操作1.
- 当栈为NULL时就结束。(这儿不需要指向结点为NULL)
1 void PostOrder1(BiTree *t)//后序的非递归算法 2 { 3 BStack st[MaxSize]; 4 /* 5 for(int i=0;i<MaxSize;i++) 6 { 7 printf("%d\n",st[i].flag); 8 } 9 结构体数组可不会初始化为0 10 */ 11 for(int i=0;i<MaxSize;i++) 12 { 13 st[i].flag=0;//标志位,判断是否访问过左右子树 ,0表示左未访问,2表示右孩子被访问过 14 } 15 int top=0; 16 do 17 { 18 while(t!=NULL&&st[top].flag==0)//左子树依次入栈,且未被访问过 19 { 20 st[top].data=t; 21 st[top].flag=1; 22 t=t->lchild; 23 top++; 24 } 25 if(st[top].flag==0)//这个就是如果已经是栈里面的就不需要在取栈的元素了 26 { 27 top--; 28 t=st[top].data; 29 } 30 if(t->rchild!=NULL&&st[top].flag!=2)//右子树不为空且未被访问过。 31 { 32 t=t->rchild; 33 st[top++].flag=2;//标记该节点的右子树被访问过。 34 } 35 else 36 { 37 printf("%c",t->data); 38 st[top].flag=0; 39 /* 40 因为这个是我逻辑上定义的栈,所以出栈操作只是把top指针改变了,没有改变里面的值,所以要手动的把 41 flag值归为初始值。如果自己定义的链栈的话,出栈就是纯粹的出栈了。 我自己的出栈就不纯粹。 42 */ 43 top--; 44 t=st[top].data; 45 } 46 }while(top!=-1); 47 }
上面是自己写的。
下面附上教材上的代码: