二叉樹的遞歸遍歷很好寫,也很好理解。但因為是遞歸程序,不可避免地需要調用系統棧,耗時較長,這里我們來探究一下二叉樹的非遞歸遍歷的算法。這種方法需要使用棧這種數據結構,這里關於棧的一些操作函數可以看成偽代碼吧,先給出線序、中序、后序遍歷的代碼即說明。
先序遍歷:
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 }
再給出使用數組模擬的函數

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 }
中序遍歷:
void InOrderTraverse(BinTree b) { InitStack(S);///初始化創建棧 BinTree p=b;///p為工作指針 while(p||!isEmpty(s)) { while(p) { Push(S,p);///中序現將結點進棧保存 p=p->lchild; }///遍歷到左下角盡頭再出棧訪問 if(!isEmpty(s))///在棧不為空的情況下,左孩子為空,彈出該結點,遍歷右孩子 { p=Pop(s); printf(" %c ",p->data); p=p->rchild;///遍歷右孩子 } } }
再給出使用數組模擬的函數

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為空繼續訪問棧頂 } } } }
數組模擬:

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 的函數:

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 }