日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构——二叉树的遍历

發布時間:2025/3/19 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构——二叉树的遍历 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

??????? “樹”是一種重要的數據結構,本文淺談二叉樹的遍歷問題,採用C語言描寫敘述。

?

一、二叉樹基礎

1)定義:有且僅有一個根結點,除根節點外,每一個結點僅僅有一個父結點,最多含有兩個子節點,子節點有左右之分。
2)存儲結構

????????二叉樹的存儲結構能夠採用順序存儲,也能夠採用鏈式存儲,當中鏈式存儲更加靈活。

??????? 在鏈式存儲結構中,與線性鏈表類似,二叉樹的每一個結點採用結構體表示,結構體包括三個域:數據域、左指針、右指針。

??????? 二叉樹在C語言中的定義例如以下:???????

struct BiTreeNode{int c;struct BiTreeNode *left;struct BiTreeNode *right; };

?

二、二叉樹的遍歷

??????? “遍歷”是二叉樹各種操作的基礎。二叉樹是一種非線性結構,其遍歷不像線性鏈表那樣easy,無法通過簡單的循環實現。

??????? 二叉樹是一種樹形結構,遍歷就是要讓樹中的全部節點被且僅被訪問一次,即按一定規律排列成一個線性隊列。二叉(子)樹是一種遞歸定義的結構,包括三個部分:根結點(N)、左子樹(L)、右子樹(R)。依據這三個部分的訪問次序對二叉樹的遍歷進行分類,總共同擁有6種遍歷方案:NLR、LNR、LRN、NRL、RNL和LNR。研究二叉樹的遍歷就是研究這6種詳細的遍歷方案,顯然依據簡單的對稱性,左子樹和右子樹的遍歷可互換,即NLR與NRL、LNR與RNL、LRN與RLN,分別相類似,因而僅僅需研究NLR、LNR和LRN三種就可以,分別稱為“先序遍歷”、“中序遍歷”和“后序遍歷”。

??????? 二叉樹遍歷通常借用“?!边@樣的數據結構實現,有兩種方式:遞歸方式及非遞歸方式。

??????? 在遞歸方式中,棧是由操作系統維護的,用戶不必關心棧的細節操作,用戶僅僅需關心“訪問順序”就可以。因而,採用遞歸方式實現二叉樹的遍歷比較easy理解,算法簡單,easy實現。

??????? 遞歸方式實現二叉樹遍歷的C語言代碼例如以下:

//先序遍歷--遞歸 int traverseBiTreePreOrder(BiTreeNode *ptree,int (*visit)(int)) {if(ptree){if(visit(ptree->c))if(traverseBiTreePreOrder(ptree->left,visit))if(traverseBiTreePreOrder(ptree->right,visit))return 1; //正常返回return 0; //錯誤返回}else return 1; //正常返回 } //中序遍歷--遞歸 int traverseBiTreeInOrder(BiTreeNode *ptree,int (*visit)(int)) {if(ptree){if(traverseBiTreeInOrder(ptree->left,visit))if(visit(ptree->c))if(traverseBiTreeInOrder(ptree->right,visit))return 1;return 0;}else return 1; } //后序遍歷--遞歸 int traverseBiTreePostOrder(BiTreeNode *ptree,int (*visit)(int)) {if(ptree){if(traverseBiTreePostOrder(ptree->left,visit))if(traverseBiTreePostOrder(ptree->right,visit))if(visit(ptree->c))return 1;return 0;}else return 1; }

??????? 以上代碼中,visit為一函數指針,用于傳遞二叉樹中對結點的操作方式,其原型為:int (*visit)(char)。

??????? 大家知道,函數在調用時,會自己主動進行棧的push,調用返回時,則會自己主動進行棧的pop。函數遞歸調用無非是對一個棧進行返回的push與pop,既然遞歸方式能夠實現二叉樹的遍歷,那么借用“?!睊裼梅沁f歸方式,也能實現遍歷。可是,這時的棧操作(push、pop等)是由用戶進行的,因而實現起來會復雜一些,并且也不easy理解,但有助于我們對樹結構的遍歷有一個深刻、清晰的理解。

??????? 在討論非遞歸遍歷之前,我們先定義棧及各種須要用到的棧操作:

//棧的定義,棧的數據是“樹結點的指針” struct Stack{BiTreeNode **top;BiTreeNode **base;int size; }; #define STACK_INIT_SIZE 100 #define STACK_INC_SIZE 10 //初始化空棧,預分配存儲空間 Stack* initStack() {Stack *qs=NULL;qs=(Stack *)malloc(sizeof(Stack));qs->base=(BiTreeNode **)calloc(STACK_INIT_SIZE,sizeof(BiTreeNode *));qs->top=qs->base;qs->size=STACK_INIT_SIZE;return qs; } //取棧頂數據 BiTreeNode* getTop(Stack *qs) {BiTreeNode *ptree=NULL;if(qs->top==qs->base)return NULL;ptree=*(qs->top-1);return ptree; } //入棧操作 int push(Stack *qs,BiTreeNode *ptree) {if(qs->top-qs->base>=qs->size){qs->base=(BiTreeNode **)realloc(qs->base,(qs->size+STACK_INC_SIZE)*sizeof(BiTreeNode *));qs->top=qs->base+qs->size;qs->size+=STACK_INC_SIZE;}*qs->top++=ptree;return 1; } //出棧操作 BiTreeNode* pop(Stack *qs) {if(qs->top==qs->base)return NULL;return *--qs->top; } //推斷棧是否為空 int isEmpty(Stack *qs) {return qs->top==qs->base; }

????????首先考慮非遞歸先序遍歷(NLR)。在遍歷某一個二叉(子)樹時,以一當前指針記錄當前要處理的二叉(左子)樹,以一個棧保存當前樹之后處理的右子樹。首先訪問當前樹的根結點數據,接下來應該依次遍歷其左子樹和右子樹,然而程序的控制流僅僅能處理其一,所以考慮將右子樹的根保存在棧里面,當前指針則指向需先處理的左子樹,為下次循環做準備;若當前指針指向的樹為空,說明當前樹為空樹,不須要做不論什么處理,直接彈出棧頂的子樹,為下次循環做準備。對應的C語言代碼例如以下:

//先序遍歷--非遞歸 int traverseBiTreePreOrder2(BiTreeNode *ptree,int (*visit)(int)) {Stack *qs=NULL;BiTreeNode *pt=NULL;qs=initStack();pt=ptree;while(pt || !isEmpty(qs)){if(pt){if(!visit(pt->c)) return 0; //錯誤返回push(qs,pt->right);pt=pt->left;}else pt=pop(qs);}return 1; //正常返回 }

??????? 相對于非遞歸先序遍歷,非遞歸的中序/后序遍歷稍復雜一點。

??????? 對于非遞歸中序遍歷,若當前樹不為空樹,則訪問其根結點之前應先訪問其左子樹,因而先將當前根節點入棧,然后考慮其左子樹,不斷將非空的根節點入棧,直到左子樹為一空樹;當左子樹為空時,不須要做不論什么處理,彈出并訪問棧頂結點,然后指向其右子樹,為下次循環做準備。

//中序遍歷--非遞歸 int traverseBiTreeInOrder2(BiTreeNode *ptree,int (*visit)(int)) {Stack *qs=NULL;BiTreeNode *pt=NULL;qs=initStack();pt=ptree;while(pt || !isEmpty(qs)){if(pt){push(qs,pt);pt=pt->left;}else{pt=pop(qs);if(!visit(pt->c)) return 0;pt=pt->right;}}return 1; } //中序遍歷--非遞歸--還有一種實現方式 int traverseBiTreeInOrder3(BiTreeNode *ptree,int (*visit)(int)) {Stack *qs=NULL;BiTreeNode *pt=NULL;qs=initStack();push(qs,ptree);while(!isEmpty(qs)){while(pt=getTop(qs)) push(qs,pt->left);pt=pop(qs);if(!isEmpty(qs)){pt=pop(qs);if(!visit(pt->c)) return 0;push(qs,pt->right);}}return 1; }

??????? 最后談談非遞歸后序遍歷。因為在訪問當前樹的根結點時,應先訪問其左、右子樹,因而先將根結點入棧,接著將右子樹也入棧,然后考慮左子樹,反復這一過程直到某一左子樹為空;假設當前考慮的子樹為空,若棧頂不為空,說明第二棧頂相應的樹的右子樹未處理,則彈出棧頂,下次循環處理,并將一空指針入棧以表示其還有一子樹已做處理;若棧頂也為空樹,說明第二棧頂相應的樹的左右子樹或者為空,或者均已做處理,直接訪問第二棧頂的結點,訪問完結點后,若棧仍為非空,說明整棵樹尚未遍歷完,則彈出棧頂,并入棧一空指針表示第二棧頂的子樹之中的一個已被處理。

//后序遍歷--非遞歸 int traverseBiTreePostOrder2(BiTreeNode *ptree,int (*visit)(int)) {Stack *qs=NULL;BiTreeNode *pt=NULL;qs=initStack();pt=ptree;while(1) //循環條件恒“真”{if(pt){push(qs,pt);push(qs,pt->right);pt=pt->left;}else if(!pt){pt=pop(qs);if(!pt){pt=pop(qs);if(!visit(pt->c)) return 0;if(isEmpty(qs)) return 1;pt=pop(qs);}push(qs,NULL);}}return 1; }


三、二叉樹的創建

??????? 談完二叉樹的遍歷之后,再來談談二叉樹的創建,這里所說的創建是指從控制臺依次(先/中/后序)輸入二叉樹的各個結點元素(此處為字符),用“空格”表示空樹。

??????? 因為控制臺輸入是保存在輸入緩沖區內,因此遍歷的“順序”就反映在讀取輸入字符的次序上。

??????? 下面是遞歸方式實現的先序創建二叉樹的C代碼。

//創建二叉樹--先序輸入--遞歸 BiTreeNode* createBiTreePreOrder() {BiTreeNode *ptree=NULL;char ch;ch=getchar();if(ch==' ')ptree=NULL;else{ptree=(struct BiTreeNode *)malloc(sizeof(BiTreeNode));ptree->c=ch;ptree->left=createBiTreePreOrder();ptree->right=createBiTreePreOrder();}return ptree; }

??????? 對于空樹,函數直接返回就可以;對于非空樹,先讀取字符并賦值給當前根結點,然后創建左子樹,最后創建右子樹。因此,要先知道當前要創建的樹是否為空,才干做對應處理,“先序”遍歷方式非常好地符合了這一點。可是中序或后序就不一樣了,更重要的是,中序或后序方式輸入的字符序列無法唯一確定一個二叉樹。我還沒有找到中序/后序實現二叉樹的創建(控制臺輸入)的類似簡單的方法,希望各位同仁網友指教哈!

?

四、執行及結果

??????? 採用例如以下的二叉樹進行測試,首先先序輸入創建二叉樹,然后依次調用各個遍歷函數。

??????? 先序輸入的格式:ABC ^ ^ D E ^ G ^ ^ F ^ ^ ^???? (當中, ^? 表示空格字符)

??????? 遍歷操作採用標準I/O庫中的putchar函數,其原型為:int putchar(int);

??????? 各種形式遍歷輸出的結果為:

??????????????? 先序:ABCDEGF

??????????????? 中序:CBEGDFA

??????????????? 后序:CGEFDBA

??????? 測試程序的主函數例如以下:

int main(int argc, char* argv[]) {BiTreeNode *proot=NULL;printf("InOrder input chars to create a BiTree: ");proot=createBiTreePreOrder(); //輸入(ABC DE G F )printf("PreOrder Output the BiTree recursively: ");traverseBiTreePreOrder(proot,putchar);printf("\n");printf("PreOrder Output the BiTree non-recursively: ");traverseBiTreePreOrder2(proot,putchar);printf("\n");printf("InOrder Output the BiTree recursively: ");traverseBiTreeInOrder(proot,putchar);printf("\n");printf("InOrder Output the BiTree non-recursively(1): ");traverseBiTreeInOrder2(proot,putchar);printf("\n");printf("InOrder Output the BiTree non-recursively(2): ");traverseBiTreeInOrder3(proot,putchar);printf("\n");printf("PostOrder Output the BiTree non-recursively: ");traverseBiTreePostOrder(proot,putchar);printf("\n");printf("PostOrder Output the BiTree recursively: ");traverseBiTreePostOrder2(proot,putchar);printf("\n");return 0; }

?

總結

以上是生活随笔為你收集整理的数据结构——二叉树的遍历的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。