二叉树的基本特性和二叉树的几种基本操作的机制_深入理解二叉树01 二叉树基础
數據結構是所有的程序員就業過程中無法回避的知識,最近在回顧數據結構的內容,因此會不定期的推出一些數據結構的文章,分享自己的筆記。
樹是數據結構中的重點,由于二叉樹又是樹中的重中之重。二叉樹的應用也非常的多,比如二叉排序樹、平衡二叉樹、紅黑樹、B樹、堆等。本篇文章是關于二叉樹的第一篇文章,主要介紹二叉樹的幾種遍歷方式以及代碼實現。
一、二叉樹的基本概念
二叉樹的定義:一棵二叉樹節點的有限集合,該集合或者為空,或者由一個根節點加上兩棵左子樹和右子樹組成,其左子樹和右子樹又分別是一棵二叉樹。
二叉樹的特點:
- 每個節點最多只有兩棵子樹,即二叉樹不存在度數大于2的節點
- 二叉樹的子樹有左、右之分,其子樹的次序不能顛倒
- 二叉樹的典型遍歷方式有四種:先序遍歷、中序遍歷、后序遍歷、層序遍歷
幾種特殊的二叉樹:
- 滿二叉樹:在一棵二叉樹中,所有分支節點都存在左子樹和右子樹,并且所有的葉節點都在同一層上。如圖1。
- 完全二叉樹:一棵具有N個節點的完全二叉樹的結構與滿二叉樹的前N個節點的結構相同。如圖2。
圖1:高度為3的滿二叉樹
圖2:高度為3的完全二叉樹
二、二叉樹的遍歷
在開始介紹遍歷之前,首先規定一下二叉樹的存儲和遍歷的接口,定義如下:
typedef struct BinaryTreeNode{ int data; BinaryTreeNode* leftChild; BinaryTreeNode* rightChild;}BinaryTreeNode, BinaryTree;class BinaryTreeOperate{public: static void createBinaryTree(BinaryTree** root); /* 遞歸方式操作 */ static void preOrderTraverse(const BinaryTree* root); static void inOrderTraverse(const BinaryTree* root); static void postOrderTraverse(const BinaryTree* root); /* 非遞歸方式操作 */ static void preOrderTraverseNonRecurve(const BinaryTree* root); static void inOrderTraverseNonRecurve(const BinaryTree* root); static void postOrderTraverseNonRecurve(const BinaryTree* root); /* 寬度優先遍歷 */ static void widthFirstTraverse(const BinaryTree* root);};測試二叉樹結構如圖3所示:
圖3:測試二叉樹
1、二叉樹的先序遍歷
二叉樹的先序遍歷:先訪問根節點,再訪問左子樹,最后訪問右子樹。
上述的二叉樹的先序遍歷結果為:10 6 4 8 14 12 16
遞歸方式實現:
void BinaryTreeOperate::preOrderTraverse(const BinaryTree* root){ if(root != nullptr) { cout << root->data << ""; BinaryTreeOperate::preOrderTraverse(root->leftChild); BinaryTreeOperate::preOrderTraverse(root->rightChild); }}非遞歸方式實現:
思考一下如何實現:首先訪問根節點,然后訪問它的左子樹;當左子樹為空時,我們要退回到上一個訪問的節點(根節點),訪問它的右子樹。如何實現退回到上一個訪問的節點的操作?有數據結構基礎的人很容易就會想到棧了。
過程:
- 訪問根節點,然后將根節點入棧
- 遍歷根節點的左子樹
- 當左子樹為空,獲取棧頂元素并出棧,然后訪問棧頂元素的右子樹
2、二叉樹的中序遍歷
二叉樹的中序遍歷:先遍歷左子樹,再訪問根節點,最后訪問右子樹。
上述的二叉樹的中序遍歷結果為:4 6 8 10 12 14 16
遞歸方式實現:
void BinaryTreeOperate::inOrderTraverse(const BinaryTree* root){ if(root != nullptr) { BinaryTreeOperate::inOrderTraverse(root->leftChild); cout << root->data << ""; BinaryTreeOperate::inOrderTraverse(root->rightChild); }}非遞歸方式實現:
中序遍歷的非遞歸實現和先序遍歷類似:
- 先將根節點入棧
- 遍歷根節點的左子樹
- 當左子樹為空時,獲取棧定元素并出棧,訪問棧頂元素,然后遍歷棧頂元素的右子樹
3、二叉樹的后序遍歷
二叉樹的后序遍歷:先遍歷左子樹,再遍歷右子樹,最后訪問根節點。
上述二叉樹的后序遍歷結果為:4 8 6 12 16 14 10
遞歸方式實現:
void BinaryTreeOperate::postOrderTraverse(const BinaryTree* root){ if(root != nullptr) { BinaryTreeOperate::postOrderTraverse(root->leftChild); BinaryTreeOperate::postOrderTraverse(root->rightChild); cout << root->data << ""; }}非遞歸方式實現:
后序遍歷的非遞歸實現稍微復雜一點,先看一下下面的分析:
對于下圖所示的這一棵二叉樹:
后序遍歷的結果為:4 8 6 12 16 14 10
后序遍歷的逆序結果為:10 14 16 12 6 8 4
想一下后序遍歷的逆序結果是如何得到的:
- 先遍歷根節點
- 然后遍歷右子樹
- 最后遍歷左子樹
思路出來了:
4、二叉樹的層序遍歷
二叉樹的層序遍歷:層序遍歷也可以被稱為寬度優先遍歷,先訪問樹的第一層節點,再訪問樹的第二層節點······。再同一層節點中,以從左到右的順序依次訪問。
上述二叉樹的層序遍歷結果為:10 6 14 4 8 12 16
代碼實現(借助隊列):
void BinaryTreeOperate::widthFirstTraverse(const BinaryTree* root){ queue nodes_queue; BinaryTreeNode* p = const_cast(root); nodes_queue.push(p); while(!nodes_queue.empty()) { p = nodes_queue.front(); nodes_queue.pop(); if(p != nullptr) { cout << p->data << ""; if(p->leftChild != nullptr) nodes_queue.push(p->leftChild); if(p->rightChild != nullptr) nodes_queue.push(p->rightChild); } }}關于二叉樹的遍歷操作:遞歸實現簡單,但是效率就比較低,因為編譯器幫你做了很多的現場保護和現場恢復的工作;非遞歸實現都借用了棧,其實也是在模仿遞歸。兩種方式都很重要。
完整代碼(包含二叉樹創建、遍歷的各種實現、main函數測試)如下:
#include #include #include #include using namespace std;// 二叉樹的每個節點的值,0表示該節點為空vector tree_data = {10, 6, 4, 0, 0, 8, 0, 0, 14, 12, 0, 0, 16, 0, 0};typedef struct BinaryTreeNode{ int data; BinaryTreeNode* leftChild; BinaryTreeNode* rightChild;}BinaryTreeNode, BinaryTree;class BinaryTreeOperate{public: static void createBinaryTree(BinaryTree** root); /* 遞歸方式操作 */ static void preOrderTraverse(const BinaryTree* root); static void inOrderTraverse(const BinaryTree* root); static void postOrderTraverse(const BinaryTree* root); /* 非遞歸方式操作 */ static void preOrderTraverseNonRecurve(const BinaryTree* root); static void inOrderTraverseNonRecurve(const BinaryTree* root); static void postOrderTraverseNonRecurve(const BinaryTree* root); /* 寬度優先遍歷 */ static void widthFirstTraverse(const BinaryTree* root);};void BinaryTreeOperate::createBinaryTree(BinaryTree **root){ static int id = 0; int tmp_data = tree_data[id++]; if (tmp_data > 0) { BinaryTreeNode *newNode = new BinaryTreeNode(); newNode->data = tmp_data; newNode->leftChild = nullptr; newNode->rightChild = nullptr; *root = newNode; BinaryTreeOperate::createBinaryTree(&((*root)->leftChild)); BinaryTreeOperate::createBinaryTree(&((*root)->rightChild)); }}void BinaryTreeOperate::preOrderTraverse(const BinaryTree* root){ if(root != nullptr) { cout << root->data << ""; BinaryTreeOperate::preOrderTraverse(root->leftChild); BinaryTreeOperate::preOrderTraverse(root->rightChild); }}void BinaryTreeOperate::preOrderTraverseNonRecurve(const BinaryTree* root){ stack nodes_stack; //root用了const修飾,直接將const賦值為non-const是不可以的 //需要先使用const_cast去除root的const限定 BinaryTreeNode* p = const_cast(root); while(p != nullptr || !nodes_stack.empty()) { if(p != nullptr) { cout << p->data << ""; nodes_stack.push(p); p = p->leftChild; } else { p = nodes_stack.top(); nodes_stack.pop(); p = p->rightChild; } }}void BinaryTreeOperate::inOrderTraverse(const BinaryTree* root){ if(root != nullptr) { BinaryTreeOperate::inOrderTraverse(root->leftChild); cout << root->data << ""; BinaryTreeOperate::inOrderTraverse(root->rightChild); }}void BinaryTreeOperate::inOrderTraverseNonRecurve(const BinaryTree* root){ stack nodes_stack; BinaryTreeNode* p = const_cast(root); while(p != nullptr || !nodes_stack.empty()) { if(p != nullptr) { nodes_stack.push(p); p = p->leftChild; } else { p = nodes_stack.top(); nodes_stack.pop(); cout << p->data << ""; p = p->rightChild; } }}void BinaryTreeOperate::postOrderTraverse(const BinaryTree* root){ if(root != nullptr) { BinaryTreeOperate::postOrderTraverse(root->leftChild); BinaryTreeOperate::postOrderTraverse(root->rightChild); cout << root->data << ""; }}void BinaryTreeOperate::postOrderTraverseNonRecurve(const BinaryTree* root){ stack nodes_stack_1, nodes_stack_2; BinaryTreeNode* p = const_cast(root); while(p != nullptr || !nodes_stack_1.empty()) { if(p != nullptr) { nodes_stack_2.push(p); nodes_stack_1.push(p); p = p->rightChild; } else { p = nodes_stack_1.top(); nodes_stack_1.pop(); p = p->leftChild; } } while(!nodes_stack_2.empty()) { p = nodes_stack_2.top(); nodes_stack_2.pop(); cout << p->data << ""; }}void BinaryTreeOperate::widthFirstTraverse(const BinaryTree* root){ queue nodes_queue; BinaryTreeNode* p = const_cast(root); nodes_queue.push(p); while(!nodes_queue.empty()) { p = nodes_queue.front(); nodes_queue.pop(); if(p != nullptr) { cout << p->data << ""; if(p->leftChild != nullptr) nodes_queue.push(p->leftChild); if(p->rightChild != nullptr) nodes_queue.push(p->rightChild); } }}int main(){ BinaryTree* root; BinaryTreeOperate::createBinaryTree(&root); cout << "先序遍歷結果: " << endl; BinaryTreeOperate::preOrderTraverse(root); cout << ""; BinaryTreeOperate::preOrderTraverseNonRecurve(root); cout << "中序遍歷結果: " << endl; BinaryTreeOperate::inOrderTraverse(root); cout << ""; BinaryTreeOperate::inOrderTraverseNonRecurve(root); cout << "后序遍歷結果: " << endl; BinaryTreeOperate::postOrderTraverse(root); cout << ""; BinaryTreeOperate::postOrderTraverseNonRecurve(root); cout << "寬度優先遍歷結果:" << endl; BinaryTreeOperate::widthFirstTraverse(root); return 0;}運行結果如圖4所示:
圖4:完整代碼的運行結果
今天的內容就到這兒了。如果對我的推、文有興趣,歡迎轉、載分、享。也可以推薦給朋友關、注哦。只推干貨,寧缺毋濫。
總結
以上是生活随笔為你收集整理的二叉树的基本特性和二叉树的几种基本操作的机制_深入理解二叉树01 二叉树基础的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手机写python脚本_手机需要,哪位大
- 下一篇: 二叉树后序遍历_LeetCode算法14