摘要:代碼實現如下二叉樹的創建與銷毀二叉樹的創建問題通過前序遍歷的數組給定一串字符串,代表的是空樹,其他的都是節點。
??本篇博客我要來和大家一起聊一聊數據結構中的二叉樹的鏈式結構的實現及相關的一些問題的介紹
??博客代碼已上傳至gitee:https://gitee.com/byte-binxin/data-structure/commit/de7024a7498be71a78c18d22b7a7caee53f3ffb4
前一篇博客介紹了二叉樹的順序結構,是通數組來存儲的,這里我們通過創建鏈式結構來存儲,在堆上申請空間,結構如下:
typedef char BTDataType;typedef struct BinaryTreeNode{ BTDataType data; struct BinaryTreeNode* left; struct BinaryTreeNode* right;}BTNode;
這里我們用最粗暴的方法創建一棵樹,就是一個節點一個節點的創建,實現代碼如下:
BTNode* CreatBinaryTree(){ BTNode* treeNode1 = (BTNode*)malloc(sizeof(BTNode)); BTNode* treeNode2 = (BTNode*)malloc(sizeof(BTNode)); BTNode* treeNode3 = (BTNode*)malloc(sizeof(BTNode)); BTNode* treeNode4 = (BTNode*)malloc(sizeof(BTNode)); BTNode* treeNode5 = (BTNode*)malloc(sizeof(BTNode)); BTNode* treeNode6 = (BTNode*)malloc(sizeof(BTNode)); treeNode1->data = "A"; treeNode2->data = "B"; treeNode3->data = "C"; treeNode4->data = "D"; treeNode5->data = "E"; treeNode6->data = "F"; treeNode1->left = treeNode2; treeNode1->right = treeNode3; treeNode2->left = treeNode4; treeNode2->right = treeNode5; treeNode3->left = treeNode6; treeNode3->right = NULL; treeNode4->left = treeNode4->right = NULL; treeNode5->left = treeNode5->right = NULL; treeNode6->left = treeNode6->right = NULL;}
如下圖:
?這里我先給大家介紹一下用遞歸實現,后面我會給大家介紹如何用棧模擬實現非遞歸。
?前序遍歷指的是先遍歷根,再遍歷左子樹,再遍歷右子樹。
?思想: 二叉樹本身就是一種遞歸結構,所以通過遞歸來遍歷這棵樹,如何遞歸遍歷呢?
是這樣的,先遍歷根,再遍歷左子樹,左子樹又可以分解為,根、左子樹和右子樹,直到把所以左子樹的部分遍歷完,然后就遍歷右子樹,右子樹又可以分解為,根、左子樹和右子樹。
假如我們構建了一個PrevOrder,這個函數是可以實現前序遍歷的,所以我們先遍歷根,剩下的左子樹和右子樹遍歷就交給這個函數去實現。
遞歸就是把一個大的問題一直分解,直到分解為最后一個小問題來解決,這就是所謂的大事化小。
了解了上面那些,我們就來看一下代碼是如何實現的,
void BinaryTreePrevOrder(BTNode* root){ // 遍歷到NULL就返回 if (root == NULL) { printf("NULL "); return; } // 先遍歷根 printf("%c ", root->data); // 左子樹交給BinaryTreePrevOrder這個函數去遍歷 BinaryTreePrevOrder(root->left); // 右子樹交給BinaryTreePrevOrder這個函數去遍歷 BinaryTreePrevOrder(root->right);}
下面我們來看一下代碼運行結果:
?中序遍歷指的是先遍歷左子樹,再遍歷根,再遍歷右子樹。
?思想: 二叉樹本身就是一種遞歸結構,所以通過遞歸來遍歷這棵樹,如何遞歸遍歷呢?
是這樣的,先遍歷左子樹,左子樹又可以分解為,左子樹、根和右子樹,直到把所以左子樹的部分遍歷完,然后就遍歷根,再遍歷右子樹,右子樹又可以分解為,左子樹、根和右子樹。
如下圖:
代碼實現如下:
void BinaryTreeInOrder(BTNode* root){ // 遍歷到NULL就返回 if (root == NULL) { printf("NULL "); return; } // 左子樹交給BinaryTreePrevOrder這個函數去遍歷 BinaryTreeInOrder(root->left); // 遍歷根 printf("%c ", root->data); // 右子樹交給BinaryTreePrevOrder這個函數去遍歷 BinaryTreeInOrder(root->right);}
代碼運行結果如下:
?中序遍歷指的是先遍歷左子樹,再遍歷根,再遍歷右子樹。
?思想: 二叉樹本身就是一種遞歸結構,所以通過遞歸來遍歷這棵樹,如何遞歸遍歷呢?
是這樣的,先遍歷左子樹,左子樹又可以分解為,左子樹、右子樹和根,直到把所以左子樹的部分遍歷完,然后遍歷右子樹,右子樹又可以分解為,左子樹、右子樹和根,最后遍歷根。
如下圖:
代碼實現如下:
void BinaryTreePostOrder(BTNode* root){ if (root == NULL) { printf("NULL "); return; } // 左子樹交給BinaryTreePrevOrder這個函數去遍歷 BinaryTreePostOrder(root->left); // 右子樹交給BinaryTreePrevOrder這個函數去遍歷 BinaryTreePostOrder(root->right); //遍歷根 printf("%c ", root->data);}
代碼運行結果如下:
? 層序遍歷: 設二叉樹的根節點所在層數為1,層序遍歷就是從所在二叉樹的根節點出發,首先訪問第一層的樹根節點,然后從左到右訪問第2層上的節點,接著是第三層的節點,以此類推,自上而下,自左至右逐層訪問樹的結點的過程就是層序遍歷。
層序遍歷用到的是隊列來解決,先將根入隊,然后把根取出,取出的同時分別再把不為空
的左節點和右節點入隊,直到隊列為空時就說明二叉樹已經遍歷完了。為了方便大家理解我在這里做了個動圖演示一下:
再看一下代碼實現如下:
void BinaryTreeLevelOrder(BTNode* root){ if (root == NULL) { printf("NULL"); return; } queue<BTNode*>q; q.push(root); while (!q.empty()) { BTNode* front = q.front(); q.pop(); printf("%c ", front->data); if (front->left) q.push(front->left); if (front->right) q.push(front->right); }}
代碼運行結果如下:
?此問題可以分解為求左子樹節點個數+右子樹節點個數+1,然后左子樹節點個數又可以繼續分,所以這里可以用遞歸來求解這個問題,下面我們就來實現一下這個接口
代碼如下:
int BinaryTreeSize(BTNode* root){ return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;}
代碼運行結果如下:
??問題可以分解為求左子樹葉子節點個數+右子樹葉子節點個數,也是一個遞歸的問題,這里就直接實現了。
代碼如下:
int BinaryTreeLeafSize(BTNode* root){ if (root == NULL) return 0; if (root->left == NULL && root->right == NULL) return 1; return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);}
運行結果如下:
??求解第k層節點個數其實就是求解左子樹的第k-1層節點個數+右子樹的第k-1層節點個數,當k==1時,就可以直接返回1,節點為空就返回0。
代碼實現如下:
int BinaryTreeLevelKSize(BTNode* root, int k){ if (k < 0) return -1; if (root == NULL) return 0; if (k == 1) return 1; return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);}
?這里我們只需要前序遍歷一遍二叉樹即可,直到找到x就返回。
代碼實現如下:
BTNode* BinaryTreeFind(BTNode* root, BTDataType x){ if (root == NULL) return NULL; if (root->data == x) return root; BTNode* leftRet = BinaryTreeFind(root->left, x); if (leftRet) return leftRet; BTNode* rightRet = BinaryTreeFind(root->right, x); if (rightRet) return rightRet;}
?問題: 通過前序遍歷的數組"ABD##E#H##CF##G##"
給定一串字符串,#代表的是空樹,其他的都是節點。
這里我們只需要前序遍歷這串字符串來構建這棵樹即可。
同樣是用遞歸來解決這個問題
treeNode->left = BinaryTreeCreate(s, pi); 讓當前這個節點的左指向給遞歸構建左子樹
**treeNode->right = BinaryTreeCreate(s, pi);**讓當前這個節點的右指向給遞歸構建左子樹
如果當前節點為空就直接返回NULL
。
代碼實現如下:
BTNode* BinaryTreeCreate(BTDataType* s, int* pi){ if (s[*pi] == "#") { (*pi)++; return NULL; } BTNode* treeNode = (BTNode*)malloc(sizeof(BTNode)); treeNode->data = s[(*pi)++]; treeNode->left = BinaryTreeCreate(s, pi); treeNode->right = BinaryTreeCreate(s, pi);}
?二叉樹的銷毀我們可以通過層序遍歷來把節點逐個釋放掉,由于與上面的層序遍歷很相似,這里就不過多介紹了,直接上代碼
void BinaryTreeDestory(BTNode** root){ if (*root == NULL) return; queue<BTNode*>q; q.push(*root); while (!q.empty()) { BTNode* front = q.front(); q.pop(); if (front->left) q.push(front->left); if (front->right) q.push(front->right); free(front); front = NULL; }}
這篇博客就主要介紹了二叉樹的鏈式結構及實現,還有一些相關的問題,二叉樹有關的問題遞歸會用到比較多,因為二叉樹本身就是由遞歸來創建的。今天就介紹到這,喜歡的話,歡迎點贊、支持和指正~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/123727.html
摘要:當集合為空時,稱該二叉樹為空二叉樹。也就是說,如果一個二叉樹的層數為,且結點總數是,則它就是滿二叉樹。完全二叉樹完全二叉樹是效率很高的數據結構,完全二叉樹是由滿二叉樹而引出來的。 ...
摘要:樹中結點的最大層次稱為樹的深度或高度。二叉樹有深度遍歷和廣度遍歷,深度遍歷有前序中序和后序三種遍歷方法。二叉樹的前序遍歷可以用來顯示目錄結構等中序遍歷可以實現表達式樹,在編譯器底層很有用后序遍歷可以用來實現計算目錄內的文件及其信息等。 樹的簡介 棧、隊列、鏈表等數據結構,都是順序數據結構。而樹是非順序數據結構。樹型結構是一類非常重要的非線性結構。直觀地,樹型結構是以分支關系定義的層次結...
閱讀 927·2021-11-23 09:51
閱讀 993·2021-11-18 10:02
閱讀 1907·2021-09-10 11:27
閱讀 3138·2021-09-10 10:51
閱讀 775·2019-08-29 15:13
閱讀 2064·2019-08-29 11:32
閱讀 2501·2019-08-29 11:25
閱讀 3044·2019-08-26 11:46