【C語言】二叉樹的實現

文章目錄

  • 前言
  • ?一、二叉樹的定義
  • 🚲二、創建二叉樹
  • 🎡三、二叉樹的銷毀
  • 🎉四、遍歷二叉樹
    • 1. 前序遍歷
    • 2. 中序遍歷
    • 3. 后序遍歷
    • 4. 層序遍歷
  • 🌲五、二叉樹的計算
    • 1. 計算二叉樹結點個數
    • 2. 計算二叉樹葉子結點的個數
    • 3. 計算二叉樹的深度
    • 4. 計算二叉樹第k層的結點個數
    • 5. 查找二叉樹中值為x的結點
    • 6. 判斷二叉樹是否為完全二叉樹
  • 🏝?六、整體代碼展示

前言

在學習二叉樹實現時,我們首先要對二叉樹基本認識有一定的了解,下面我總結了以下幾點有關二叉樹的性質以及特點:
🎈每一個節點最多有兩棵子樹,不存在度大于2的節點。
🎈左右子樹是有順序的,其次序不能顛倒。
🎈二叉樹一般有四種形態,分別為:空二叉樹,只有一個根節點,根結點只有左子樹和根節點只有右子樹。
🎈二叉樹常用的三種性質:1)二叉樹的第 i 層上最多有2 ^ (i - 1)個節點;
2)深度為K的二叉樹最多有2 ^ (k - 1)個節點。
3)度為0的節點個數比度為2的節點個數多一個。

?一、二叉樹的定義

二叉樹通常以結構體的形式定義,其結構體內容包括三部分:本節點所存儲的值、左孩子節點的指針以及右孩子節點的指針。這里需要注意,子節點必須使用指針,就像我們定義結構體鏈表一樣,下一個節點必須使用地址的方式存在在結構體當中。

typedef int BTDateType;typedef struct BinaryTreeNode
{BTDateType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;

🚲二、創建二叉樹

當我們對二叉樹的掌握還不夠深入時,我們也可以創建一棵簡單的二叉樹,減少時間成本。

// 手搓一個二叉樹
BTNode* BuyNode(int x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail");return NULL;}node->data = x;node->left = NULL;node->right = NULL;
}BTNode* CreatBinaryTree()
{BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}

而真正的二叉樹創建的過程是這樣的:首先給出一個數組,將要創建的元素放在數組里。然后通過遍歷(前 或 中 或 后序遍歷)的順序訪問并創建二叉樹每個節點,最后返回根節點的地址即創建完成。
我們假設通過前序序列的方式訪問并創建二叉樹:

// 創建樹,按前序遍歷的順序
BTNode* BinaryTreeCreate(BTDateType* a, int* pi) {if (a[*pi] != '#') // '#'代表葉子結點{BTNode* root = (BTNode*)malloc(sizeof(BTNode));root->data = a[*pi];(*pi)++;root->left = BinaryTreeCreate(a, pi);(*pi)++;root->right = BinaryTreeCreate(a, pi);return root;}else {return NULL;}
}

🎡三、二叉樹的銷毀

// 銷毀
void BinaryTreeDestory(BTNode* root)
{if (root){BinaryTreeDestory(root->left);BinaryTreeDestory(root->right);free(root);root = NULL;}
}

🎉四、遍歷二叉樹

在這里插入圖片描述
前序遍歷,中序遍歷和后序遍歷,實際上就是指根節點在子節點的先中后的順序不同。以上圖為例:
前序序列:A、B、D、E、H、C、F、G

中序遍歷:D、B、H、E、A、F、C、G

后序遍歷:D、H、E、B、F、G、C、A

這三種遍歷方式,在代碼上面還是非常相似的,只不過遞歸的順序不同。

1. 前序遍歷

先遍歷根結點,再遍歷左子樹,最后遍歷右子樹。

// 前序遍歷
void PrevOrder(BTNode* root)
{if (root == NULL){printf("N "); //打印空節點數據return;}printf("%d ", root->data); // 輸出節點數據PrevOrder(root->left); //遞歸遍歷左子樹節點的數據PrevOrder(root->right); //遞歸遍歷右子樹節點的數據
}

2. 中序遍歷

先遍歷左子樹,再遍歷根結點,最后遍歷右子樹。

// 中序遍歷
void InOrder(BTNode* root)
{if (root == NULL){printf("N "); //打印空節點數據return;}InOrder(root->left); //遞歸遍歷左子樹節點的數據printf("%d ", root->data); //輸出節點數據InOrder(root->right); //遞歸遍歷右子樹節點的數據
}

3. 后序遍歷

先遍歷左子樹,再遍歷右子樹,最后遍歷根結點。

// 后序遍歷
void EndingepilogueOrder(BTNode* root)
{if (root == NULL){printf("N "); //打印空節點數據return;}EndingepiloguePrevOrder(root->left); //遞歸遍歷左子樹節點的數據EndingepiloguePrevOrder(root->right); //遞歸遍歷右子樹節點的數據printf("%d ", root->data); //輸出節點數據
}

4. 層序遍歷

層序遍歷的做法和上述遍歷做法不同,不能簡單的調用遞歸來遍歷,而是要借用到隊列來輔助實現。隊列的實現我就不在敘述了,層序遍歷代碼所示:

// 層序遍歷
void BinaryTreeLevelOrder(BTNode* root)
{Quene q;QueneInit(&q);if (root){QuenePush(&q, root); //存入根節點}while (!QueneEmpty(&q)) //隊列不為空就循環{BTNode* front = QueneFront(&q); //取出隊列中的第一個節點QuenePop(&q); //刪除第一個節點printf("%d ", front->data); //打印取出來第一個節點的數據if (front->left){QuenePush(&q, front->left); //如果左子樹不為空,就將左子樹存入隊列}if (front->right){QuenePush(&q, front->right); //如果右子樹不為空,就將右子樹存入隊列}}QueneDesTroy(&q);
}

🌲五、二叉樹的計算

1. 計算二叉樹結點個數

計算二叉樹的結點個數,只需要將左子樹的結點個數加上右子樹的結點個數,最后再加上根結點就完成了。

int TreeSide(BTNode* root)
{return root == NULL ? 0 : TreeSide(root->left) + TreeSide(root->right) + 1; //運用條件表達式,如果根結點為空就返回0,否則就遞歸調用遍歷左子樹和右子樹的結點個數,兩者相加,最后再加一個最上面的根結點。
}

2. 計算二叉樹葉子結點的個數

首先要明白什么是葉子結點,實際上就是度為0的結點即孩子結點。
在這里插入圖片描述
如上圖,D、H、F、G都為葉子結點。代碼展示:

int TreeLeafSize(BTNode* root)
{if (root == NULL){return 0; //空樹返回0}else if (TreeLeafSize(root->left)== NULL && TreeLeafSize(root->right) == NULL){return 1; //只含有根節點就返回1}return TreeLeafSize(root->left) + TreeLeafSize(root->right); ///遞歸調用遍歷左子樹和右子樹的葉子數,兩者相加
}

3. 計算二叉樹的深度

什么是二叉樹的深度呢?簡單的來說就是左子樹或者右子樹的深度+1。

// 求樹的深度
int TreeHight(BTNode* root)
{if (root == NULL){return 0;}int highleft = TreeHight(root->left); //獲取左子樹的深度int highright = TreeHight(root->right); //獲得右子樹的深度return highleft > highright ? highleft + 1 : highright + 1; //運用條件表達式,返回左子樹和右子樹中較大的深度+1
}

4. 計算二叉樹第k層的結點個數

實現這一操作的核心思路,就是要知道:求當前樹的第k層結點個數 = 左子樹的第k - 1層的結點個數 + 右子樹的第k-1層的結點個數。

// 二叉樹第k層節點個數
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0; // 空樹返回0}if (k == 1){return 1; //第一層為根節點返回1}return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

5. 查找二叉樹中值為x的結點

這里需要注意的是,我們要記錄查找到的結點,否則當我們想要返回所找到的結點數據,卻發現又要重新遞歸去找,時間會消耗好幾倍,因此需要記錄找到的結點數據

BTNode* BinaryTreeFind(BTNode* root, BTDateType x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}BTNode* left = BinaryTreeFind(root->left, x);if (left != NULL)return left;BTNode* right = BinaryTreeFind(root->right, x);if (right != NULL)return right;// 左右子樹都沒有return NULL;
}

6. 判斷二叉樹是否為完全二叉樹

按照層序遍歷的方式遍歷完全二叉樹,當我們遍歷到空結點時,就開始判斷。如果隊列中還有空,就不是完全二叉樹

// 判斷二叉樹是否為完全二叉樹
bool BinaryTreeComplete(BTNode* root)
{Quene q;QueneInit(&q);if (root){QuenePush(&q, root);}while (!QueneEmpty(&q)){BTNode* front = QueneFront(&q);QuenePop(&q);// 遇到第一個空就開始判斷,如果隊列中還有空,就不是完全二叉樹if (front == NULL){break;}QuenePush(&q, front->left);QuenePush(&q, front->right);}while (!QueneEmpty(&q)){BTNode* front = QueneFront(&q);QuenePop(&q);// 如果有非空,就不是完全二叉樹if (front){QueneDesTroy(&q);return false;}}QueneDesTroy(&q);return true;
}

🏝?六、整體代碼展示

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include "Quene.h"typedef int BTDateType;typedef struct BinaryTreeNode
{BTDateType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;// 手搓一個二叉樹BTNode* BuyNode(int x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail");return NULL;}node->data = x;node->left = NULL;node->right = NULL;
}BTNode* CreatBinaryTree()
{BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}// 銷毀
void BinaryTreeDestory(BTNode* root)
{if (root){BinaryTreeDestory(root->left);BinaryTreeDestory(root->right);free(root);root = NULL;}
}// 層序遍歷
void BinaryTreeLevelOrder(BTNode* root)
{Quene q;QueneInit(&q);if (root){QuenePush(&q, root);}while (!QueneEmpty(&q)){BTNode* front = QueneFront(&q);QuenePop(&q);printf("%d ", front->data);if (front->left){QuenePush(&q, front->left);}if (front->right){QuenePush(&q, front->right);}}QueneDesTroy(&q);
}// 前序遍歷
void PrevOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}printf("%d ", root->data);PrevOrder(root->left);PrevOrder(root->right);
}// 中序遍歷
void InPrevOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}InPrevOrder(root->left);printf("%d ", root->data);InPrevOrder(root->right);
}// 后序遍歷
void EndingepiloguePrevOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}EndingepiloguePrevOrder(root->left);EndingepiloguePrevOrder(root->right);printf("%d ", root->data);
}int TreeSide(BTNode* root)
{return root == NULL ? 0 : TreeSide(root->left) + TreeSide(root->right) + 1;
}// 求葉子結點的個數
int TreeLeafSize(BTNode* root)
{if (root == NULL){return 0;}else if (TreeLeafSize(root->left)== NULL && TreeLeafSize(root->right) == NULL){return 1;}return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}// 求樹的深度
int TreeHight(BTNode* root)
{if (root == NULL){return 0;}int highleft = TreeHight(root->left);int highright = TreeHight(root->right);return highleft > highright ? highleft + 1 : highright + 1;
}// 二叉樹第k層節點個數
int BinaryTreeLevelKSize(BTNode* root, int k)
{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, BTDateType x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}BTNode* left = BinaryTreeFind(root->left, x);if (left != NULL)return left;BTNode* right = BinaryTreeFind(root->right, x);if (right != NULL)return right;// 左右子樹都沒有return NULL;
}// 判斷二叉樹是否為完全二叉樹
bool BinaryTreeComplete(BTNode* root)
{Quene q;QueneInit(&q);if (root){QuenePush(&q, root);}while (!QueneEmpty(&q)){BTNode* front = QueneFront(&q);QuenePop(&q);// 遇到第一個空就開始判斷,如果隊列中還有空,就不是完全二叉樹if (front == NULL){break;}QuenePush(&q, front->left);QuenePush(&q, front->right);}while (!QueneEmpty(&q)){BTNode* front = QueneFront(&q);QuenePop(&q);// 如果有非空,就不是完全二叉樹if (front){QueneDesTroy(&q);return false;}}QueneDesTroy(&q);return true;
}int main()
{BTNode* root = CreatBinaryTree();PrevOrder(root);printf("\n");InPrevOrder(root);printf("\n");EndingepiloguePrevOrder(root);printf("\n");printf("TreeSide:%d\n", TreeSide(root));printf("TreeLeafSize:%d\n", TreeLeafSize(root));printf("TreeHight:%d\n", TreeHight(root));printf("BinaryTreeFind:%p\n", BinaryTreeFind(root,3));printf("BinaryTreeLevelKSize:%d\n", BinaryTreeLevelKSize(root, 3));printf("\n");BinaryTreeLevelOrder(root);return 0;
}

今天的分享就到這里啦,如果感覺內容不錯,記得一鍵三連噢。創作不易,感謝大家的支持,我們下次再見!ヾ( ̄▽ ̄)ByeBye

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/16047.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/16047.shtml
英文地址,請注明出處:http://en.pswp.cn/web/16047.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

一、Elasticsearch介紹與部署

目錄 一、什么是Elasticsearch 二、安裝Elasticsearch 三、配置es 四、啟動es 1、下載安裝elasticsearch的插件head 2、在瀏覽器&#xff0c;加載擴展程序 3、運行擴展程序 4、輸入es地址就可以了 五、Elasticsearch 創建、查看、刪除索引、創建、查看、修改、刪除文檔…

【MySQL】——并發控制

&#x1f4bb;博主現有專欄&#xff1a; C51單片機&#xff08;STC89C516&#xff09;&#xff0c;c語言&#xff0c;c&#xff0c;離散數學&#xff0c;算法設計與分析&#xff0c;數據結構&#xff0c;Python&#xff0c;Java基礎&#xff0c;MySQL&#xff0c;linux&#xf…

計算機畢業設計 | springboot+vue房屋租賃管理系統(附源碼)

1&#xff0c;緒論 1.1 課題來源 隨著社會的不斷發展以及大家生活水平的提高&#xff0c;越來越多的年輕人選擇在大城市發展。在大城市發展就意味著要在外面有一處安身的地方。在租房的過程中&#xff0c;大家也面臨著各種各樣的問題&#xff0c;比如需要費時費力去現場看房&…

oj項目后端分析

1.菜單管理 我們菜單管理有菜單表(sys_menu)&#xff0c;還有用戶角色表&#xff08;sys_role&#xff09;&#xff0c;菜單表是用于管理我們用戶所擁有的權限&#xff0c;不同的用戶所看到的頁面是不一樣的&#xff0c;由于一些用戶他能夠看到題庫管理和考題管理&#xff0c;還…

Anaconda Anaconda支持什么編程語言的環境配置

Anaconda是一個數據科學和機器學習的開發環境&#xff0c;它支持多種編程語言的環境配置&#xff0c;包括&#xff1a; Python&#xff1a;Anaconda默認安裝了Python和必需的Python庫&#xff0c;可以方便地進行Python編程和數據分析。 R&#xff1a;Anaconda也可以配置R語言環…

Aws EC2 + Aws Cli + Terraform

1 什么是 Terraform&#xff1f; Terraform 是由 HashiCorp 創建的“基礎架構即代碼”(Infrastructure-as-Code&#xff0c;IaC)開源工具。Terraform 的配置語言是 HashiCorp Configuration Language&#xff08;HCL&#xff09;&#xff0c;用來替代更加冗長的 JSON 和 XML 等…

SpringBoot注解--09--idea創建spring boot項目,java版本只能選擇17和21

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 idea創建spring boot項目1.問題描述2.原因3.解決方法方案一&#xff1a;升級JDK版本至17或更高方案二&#xff1a;替換Spring初始化的源https://start.aliyun.com i…

實時計算及異構計算隨筆筆記

3、異構計算的典型應用 異構計算并不神秘&#xff0c;目前已滲透各個領域&#xff0c;不僅是PC領域&#xff0c;也包括了手持移動設備領域、行業領域&#xff0c;甚至是云計算、分布式計算領域。事實上&#xff0c;異構計算至少在應用端&#xff08;前臺&#xff09;并不像它的…

Android 運行時權限

Android 6.0 及以后&#xff0c;如果你的應用需要用到一些危險權限&#xff0c;那么這些權限必須手動申請。 具體危險權限有哪些&#xff0c;可以通過下面這篇文章自行查詢到&#xff1a; 使用 adb 命令列出設備所有危險權限 例如&#xff0c;讀寫文件就涉及到兩個危險權限&am…

Unity 中獲取調用者方法名

介紹 在 Unity 開發中&#xff0c;有時需要在代碼中獲取當前方法的調用者方法名&#xff0c;以便進行日志記錄、調試等操作。本教程將詳細介紹如何使用 C# 中的 StackTrace 類來實現這一功能&#xff0c;并將其封裝成一個便捷的工具類&#xff0c;以方便在項目中的任何地方…

ES的安裝以及配置+ik分詞

環境&#xff1a;windows10、ES&#xff08;8.13.3&#xff09;、Kibana&#xff08;8.13.3&#xff09;、Logstash&#xff08;8.13.3&#xff09;、ik&#xff08;8.13.3&#xff09; 1.下載安裝ES Download Elasticsearch | ElasticDownload Elasticsearch or the complet…

AI預測體彩排3采取888=3策略+和值012路一縮定乾坤測試5月26日預測第2彈

今天繼續基于8883的大底進行測試&#xff0c;昨天的預測已成功命中&#xff01;今天繼續測試&#xff0c;按照排三前面的規律&#xff0c;感覺要出對子了&#xff0c;所以本次預測不再殺對子&#xff0c;將采用殺一個和尾來代替。好了&#xff0c;直接上結果吧~ 首先&#xff0…

mongoengine,一個非常實用的 Python 庫!

更多Python學習內容&#xff1a;ipengtao.com 大家好&#xff0c;今天為大家分享一個超酷的 Python 庫 - mongoengine。 Github地址&#xff1a;https://github.com/MongoEngine/mongoengine 在現代應用程序開發中&#xff0c;NoSQL數據庫因其靈活性和高性能而廣受歡迎。MongoD…

軟件需求規范說明模板

每個軟件開發組織都會為自己的項目選用一個或多個標準的軟件需求規范說明模板。有許多軟件需求規范說明模板可以使用(例如ISO/IEC/IEEE2011;Robertson and Robertson2013)。如果你的組織要處理各種類型或規模的項目&#xff0c;例如新的大型系統開發或是對現有系統進行微調&…

concurrency 并行編程

Goroutine go語言的魅力所在&#xff0c;高并發。 線程是操作系統調度的一種執行路徑&#xff0c;用于在處理器執行我們在函數中編寫的代碼。一個進程從一個線程開始&#xff0c;即主線程&#xff0c;當該線程終止時&#xff0c;進程終止。這是因為主線程是應用程序的原點。然后…

紅黑樹封裝map和set

紅黑樹源代碼 我們將由下列的KV模型紅黑樹來模擬封裝STL庫中的map和set 注意&#xff1a;為了實現封裝map和set&#xff0c;我們需要對下列源碼進行優化。 #pragma once #include<iostream> using namespace std; //枚舉類型的顏色分類 enum Colour {RED,BLACK };//定…

【Python爬蟲】圖片驗證碼的處理

什么是圖片驗證碼&#xff1f; 驗證碼&#xff08;CAPTCHA&#xff09;是&#xff02;Completely Automated Public Turing test to tell Computers and HumansApart”&#xff08;全自動區分計算機和人類的圖靈測試&#xff09;的縮寫&#xff0c;是一種區分用戶是計算機還是人…

Markdown魔法手冊:解鎖高效寫作的新技能

邊使用邊更新0.0... 文章目錄 一、如何在Markdown中插入表情&#xff1f;二、文字樣式設置1.文本顏色設置2.文本字號設置3.文本字體設置4. 實戰演練5.黃色高亮 一、如何在Markdown中插入表情&#xff1f; 在Markdown中插入表情&#xff08;emoji&#xff09;的方法取決于你使用…

如何提升百度小程序的收錄?百度小程序如何做優化?

? 如何通過百度小程序獲得更多的自然流量&#xff1f;這是做百度小程序肯定要考慮的問題&#xff0c;做百度小程序的目的就是想借助百度生態&#xff0c;做相應的關鍵詞給自己的小程序引流&#xff0c;如何把流量給做起來呢&#xff0c;接下來我從不同的方面給大家進行分析講解…

最新ChatGpt Desktop for Mac 安裝使用教程

1. 下載地址 請點擊鏈接下載 ChatGPT Desktop for MacOS 2. 使用要求 MacOS 版本 14需要時M1芯片的&#xff0c;如果你是因特爾的暫時還還不行 就算下載了也會出現下面的異常 3. 獲取權限資格 目前 ChatGPT MacOS Desktop還不是全量開放的, 如果你沒有收到通知說明你還沒…