先序遍歷的非遞歸算法
從樹的根結點出發,遍歷左孩子的同時,先將每個結點的右孩子壓棧。當遇到結點沒有左孩子的時候,取棧頂的右孩子。重復以上過程。實現代碼函數:
//先序遍歷非遞歸算法 void PreOrderTraverse(BiTree Tree){ BiTNode* a[20];//定義一個順序棧 BiTNode * p;//臨時指針 push(a, Tree);//根結點進棧 while (top!=-1) { p=getTop(a);//取棧頂元素 pop();//彈棧 while (p) { displayElem(p);//調用結點的操作函數 //如果該結點有右孩子,右孩子進棧 if (p->rchild) { push(a,p->rchild); } p=p->lchild;//一直指向根結點最后一個左孩子 } } }
中序遍歷的非遞歸算法
從根結點開始,遍歷左孩子同時壓棧,當遍歷結束,說明當前遍歷的結點沒有左孩子,從棧中取出來調用操作函數,然后訪問該結點的右孩子,繼續以上重復性的操作。實現代碼函數:
//中序遍歷非遞歸算法 void InOrderTraverse1(BiTree Tree){ BiTNode* a[20];//定義一個順序棧 BiTNode * p;//臨時指針 push(a, Tree);//根結點進棧 while (top!=-1) {//top!=-1說明棧內不為空,程序繼續運行 while ((p=getTop(a)) &&p){//取棧頂元素,且不能為NULL push(a, p->lchild);//將該結點的左孩子進棧,如果沒有左孩子,NULL進棧 } pop();//跳出循環,棧頂元素肯定為NULL,將NULL彈棧 if (top!=-1) { p=getTop(a);//取棧頂元素 pop();//棧頂元素彈棧 displayElem(p); push(a, p->rchild);//將p指向的結點的右孩子進棧 } } }
補:中序遍歷非遞歸算法的另一種實現
中序遍歷過程中,只需將每個結點的左子樹壓棧即可,右子樹不需要壓棧。當結點的左子樹遍歷完成后,只需要以棧頂結點的右孩子為根結點,繼續循環遍歷即可。實現代碼:
void InOrderTraverse2(BiTree Tree){ BiTNode* a[20];//定義一個順序棧 BiTNode * p;//臨時指針 p=Tree; //當p為NULL或者棧為空時,表明樹遍歷完成 while (p || top!=-1) { //如果p不為NULL,將其壓棧並遍歷其左子樹 if (p) { push(a, p); p=p->lchild; } //如果p==NULL,表明左子樹遍歷完成,需要遍歷上一層結點的右子樹 else{ p=getTop(a); pop(); displayElem(p); p=p->rchild; } } }
后序遍歷的非遞歸算法
后序遍歷是在遍歷完當前結點的左右孩子之后,才調用操作函數,所以需要在操作結點進棧時,為每個結點配備一個標志位。當遍歷該結點的左孩子時,設置當前結點的標志位為 0,進棧;當要遍歷該結點的右孩子時,設置當前結點的標志位為 1,進棧。這樣,當遍歷完成,該結點彈棧時,查看該結點的標志位的值:如果是 0,表示該結點的右孩子還沒有遍歷;反之如果是 1,說明該結點的左右孩子都遍歷完成,可以調用操作函數。
實現代碼函數:
//后序遍歷函數 void PostOrderTraverse(BiTree Tree){ SNode a[20];//定義一個順序棧 BiTNode * p;//臨時指針 int tag; SNode sdata; p=Tree; while (p||top!=-1) { while (p) { //為該結點入棧做准備 sdata.p=p; sdata.tag=0;//由於遍歷是左孩子,設置標志位為0 postpush(a, sdata);//壓棧 p=p->lchild;//以該結點為根結點,遍歷左孩子 } sdata=a[top];//取棧頂元素 pop();//棧頂元素彈棧 p=sdata.p; tag=sdata.tag; //如果tag==0,說明該結點還沒有遍歷它的右孩子 if (tag==0) { sdata.p=p; sdata.tag=1; postpush(a, sdata);//更改該結點的標志位,重新壓棧 p=p->rchild;//以該結點的右孩子為根結點,重復循環 } //如果取出來的棧頂元素的tag==1,說明此結點左右子樹都遍歷完了,可以調用操作函數了 else{ displayElem(p); p=NULL; } } }
非遞歸算法的完整實現
#include <stdio.h> #include <string.h> #define TElemType int int top=-1;//top變量時刻表示棧頂元素所在位置 //構造結點的結構體 typedef struct BiTNode{ TElemType data;//數據域 struct BiTNode *lchild,*rchild;//左右孩子指針 }BiTNode,*BiTree; //初始化樹的函數 void CreateBiTree(BiTree *T){ *T=(BiTNode*)malloc(sizeof(BiTNode)); (*T)->data=1; (*T)->lchild=(BiTNode*)malloc(sizeof(BiTNode)); (*T)->rchild=(BiTNode*)malloc(sizeof(BiTNode)); (*T)->lchild->data=2; (*T)->lchild->lchild=(BiTNode*)malloc(sizeof(BiTNode)); (*T)->lchild->rchild=(BiTNode*)malloc(sizeof(BiTNode)); (*T)->lchild->rchild->data=5; (*T)->lchild->rchild->lchild=NULL; (*T)->lchild->rchild->rchild=NULL; (*T)->rchild->data=3; (*T)->rchild->lchild=(BiTNode*)malloc(sizeof(BiTNode)); (*T)->rchild->lchild->data=6; (*T)->rchild->lchild->lchild=NULL; (*T)->rchild->lchild->rchild=NULL; (*T)->rchild->rchild=(BiTNode*)malloc(sizeof(BiTNode)); (*T)->rchild->rchild->data=7; (*T)->rchild->rchild->lchild=NULL; (*T)->rchild->rchild->rchild=NULL; (*T)->lchild->lchild->data=4; (*T)->lchild->lchild->lchild=NULL; (*T)->lchild->lchild->rchild=NULL; } //前序和中序遍歷使用的進棧函數 void push(BiTNode** a,BiTNode* elem){ a[++top]=elem; } //彈棧函數 void pop( ){ if (top==-1) { return ; } top--; } //模擬操作結點元素的函數,輸出結點本身的數值 void displayElem(BiTNode* elem){ printf("%d ",elem->data); } //拿到棧頂元素 BiTNode* getTop(BiTNode**a){ return a[top]; } //先序遍歷非遞歸算法 void PreOrderTraverse(BiTree Tree){ BiTNode* a[20];//定義一個順序棧 BiTNode * p;//臨時指針 push(a, Tree);//根結點進棧 while (top!=-1) { p=getTop(a);//取棧頂元素 pop();//彈棧 while (p) { displayElem(p);//調用結點的操作函數 //如果該結點有右孩子,右孩子進棧 if (p->rchild) { push(a,p->rchild); } p=p->lchild;//一直指向根結點最后一個左孩子 } } } //中序遍歷非遞歸算法 void InOrderTraverse1(BiTree Tree){ BiTNode* a[20];//定義一個順序棧 BiTNode * p;//臨時指針 push(a, Tree);//根結點進棧 while (top!=-1) {//top!=-1說明棧內不為空,程序繼續運行 while ((p=getTop(a)) &&p){//取棧頂元素,且不能為NULL push(a, p->lchild);//將該結點的左孩子進棧,如果沒有左孩子,NULL進棧 } pop();//跳出循環,棧頂元素肯定為NULL,將NULL彈棧 if (top!=-1) { p=getTop(a);//取棧頂元素 pop();//棧頂元素彈棧 displayElem(p); push(a, p->rchild);//將p指向的結點的右孩子進棧 } } } //中序遍歷實現的另一種方法 void InOrderTraverse2(BiTree Tree){ BiTNode* a[20];//定義一個順序棧 BiTNode * p;//臨時指針 p=Tree; //當p為NULL或者棧為空時,表明樹遍歷完成 while (p || top!=-1) { //如果p不為NULL,將其壓棧並遍歷其左子樹 if (p) { push(a, p); p=p->lchild; } //如果p==NULL,表明左子樹遍歷完成,需要遍歷上一層結點的右子樹 else{ p=getTop(a); pop(); displayElem(p); p=p->rchild; } } } //后序遍歷非遞歸算法 typedef struct SNode{ BiTree p; int tag; }SNode; //后序遍歷使用的進棧函數 void postpush(SNode *a,SNode sdata){ a[++top]=sdata; } //后序遍歷函數 void PostOrderTraverse(BiTree Tree){ SNode a[20];//定義一個順序棧 BiTNode * p;//臨時指針 int tag; SNode sdata; p=Tree; while (p||top!=-1) { while (p) { //為該結點入棧做准備 sdata.p=p; sdata.tag=0;//由於遍歷是左孩子,設置標志位為0 postpush(a, sdata);//壓棧 p=p->lchild;//以該結點為根結點,遍歷左孩子 } sdata=a[top];//取棧頂元素 pop();//棧頂元素彈棧 p=sdata.p; tag=sdata.tag; //如果tag==0,說明該結點還沒有遍歷它的右孩子 if (tag==0) { sdata.p=p; sdata.tag=1; postpush(a, sdata);//更改該結點的標志位,重新壓棧 p=p->rchild;//以該結點的右孩子為根結點,重復循環 } //如果取出來的棧頂元素的tag==1,說明此結點左右子樹都遍歷完了,可以調用操作函數了 else{ displayElem(p); p=NULL; } } } int main(){ BiTree Tree; CreateBiTree(&Tree); printf("前序遍歷: \n"); PreOrderTraverse(Tree); printf("\n中序遍歷算法1: \n"); InOrderTraverse1(Tree); printf("\n中序遍歷算法2: \n"); InOrderTraverse2(Tree); printf("\n后序遍歷: \n"); PostOrderTraverse(Tree); }
運行結果
前序遍歷:
1 2 4 5 3 6 7
中序遍歷算法1:
4 2 5 1 6 3 7
中序遍歷算法2:
4 2 5 1 6 3 7
后序遍歷:
4 5 2 6 7 3 1
1 2 4 5 3 6 7
中序遍歷算法1:
4 2 5 1 6 3 7
中序遍歷算法2:
4 2 5 1 6 3 7
后序遍歷:
4 5 2 6 7 3 1
