數據結構35:二叉樹前序遍歷、中序遍歷和後序遍歷
阿新 • • 發佈:2018-05-16
tdi 代碼 nod 完成 循環 同時 reat pan 設置
實現代碼函數:
實現代碼函數:
實現代碼:
這樣,當遍歷完成,該結點彈棧時,查看該結點的標誌位的值:如果是 0,表示該結點的右孩子還沒有遍歷;反之如果是 1,說明該結點的左右孩子都遍歷完成,可以調用操作函數。
實現代碼函數:
遞歸算法底層的實現使用的是棧存儲結構,所以可以直接使用棧寫出相應的非遞歸算法。
先序遍歷的非遞歸算法
從樹的根結點出發,遍歷左孩子的同時,先將每個結點的右孩子壓棧。當遇到結點沒有左孩子的時候,取棧頂的右孩子。重復以上過程。實現代碼函數:
// 先序遍歷非遞歸算法 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:二叉樹前序遍歷、中序遍歷和後序遍歷