1. 程式人生 > >數據結構35:二叉樹前序遍歷、中序遍歷和後序遍歷

數據結構35:二叉樹前序遍歷、中序遍歷和後序遍歷

tdi 代碼 nod 完成 循環 同時 reat pan 設置

遞歸算法底層的實現使用的是棧存儲結構,所以可以直接使用棧寫出相應的非遞歸算法。

先序遍歷的非遞歸算法

從樹的根結點出發,遍歷左孩子的同時,先將每個結點的右孩子壓棧。當遇到結點沒有左孩子的時候,取棧頂的右孩子。重復以上過程。

實現代碼函數:
// 先序遍歷非遞歸算法
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;     }     else   // 如果p=NULL,表明左子樹遍歷完成,需要遍歷上一層節點的右子樹
    {       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;  //以該結點的右孩子為根結點,重復循環     }     else // 如果取出來的棧頂元素tag == 1,說明此節點左右子樹都遍歷完了,可以調用操作函數了
    {       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;     }     else // 如果p == NULL,表明左子樹遍歷完成,需要遍歷上一層節點的右子樹
    {       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;     //以該結點的右孩子為根結點,重復循環     }     else  // 如果取出來的棧頂元素tag==1,說明此結點左右子樹都遍歷完了,可以調用操作函數了
    {       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

數據結構35:二叉樹前序遍歷、中序遍歷和後序遍歷