數據結構初階-二叉樹鏈式

目錄

1.概念與結構

2.二叉數鏈式的實現

2.1遍歷規則

2.2申請內存空間

2.3手動構建一棵二叉樹

2.4二叉樹結點的個數

2.5二叉樹葉子結點的個數

2.6二叉樹第K層結點個數

2.7二叉樹的高度

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

2.9二叉樹的銷毀

3.層序遍歷

3.1概念

3.2層序遍歷的實現

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

5.總結


1.概念與結構

用鏈表來表示一棵二叉樹,即用鏈表來指示元素的邏輯關系。通常的方法是鏈表中每一個由三個域組成:數據域,左指針域和右指針域。左右指針分別用來給出該結點的左孩子和右孩子所在的鏈結點的存儲地址,最終結點的結構是下面所示:

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

由于根結點的左子樹分別又是由子樹結點、子樹結點的左子樹、子樹結點的右子樹組成的。因此二叉樹定義是遞歸式的,鏈式二叉樹的操作中基本都是按照該概念實現的。所以我們在二叉樹的各種操作中都要用到遞歸操作,所以代碼比較簡單,但是比較難寫出來!

2.二叉數鏈式的實現

2.1遍歷規則

(1)前序遍歷

訪問根結點的操作發生在左右結點之前,即我們先遍歷更結點然后再是左結點,最后才是右結點。

我們從A開始進入二叉樹,先打印A結點的數據,再打印A結點的左孩子即為B,再同理推出D,由于D的左孩子為NULL,打印完NULL后再進入D的右孩子,又為NULL,然后返回到B ,B 的左孩子已經打印完了,所以我們開始打印B的右孩子,由于為NULL,所以我們返回到B,B 子樹已經全部遍歷完了,所以我們開始遍歷C 子樹,以此類推,最終打印出順序為:ABDNULLNULLNULLCENULLNULLFNULLNULL這就是我們所說的前序遍歷。

代碼實現:

//前序遍歷
void PreOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}printf("%c", root->data);//我們主要是打印出這個結點的位置,所以我們用字母來表示這個//我們要把開始typedef的int 改為charPreOrder(root->left);PreOrder(root->right);
}

(2)中序遍歷

中序遍歷是先從左結點開始遍歷后面是根結點最后是右結點。容易知道上副圖的遍歷結果為ABDNULLNULLNULLCENULLNULLFNULLNULL,所以最終代碼實現是:

//中序遍歷
void InOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%c ", root->data);InOrder(root->right);
}

(3)后序遍歷

后序遍歷是先去遍歷左結點,再是右結點最后才是根結點,所以開始的運行結果為NULL NULL D NULL B NULL NULL E NULL NULL F C A,代碼實現:

//后序遍歷
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%c ", root->data);
}

2.2申請內存空間

我們需要先malloc一個結點大小的空間并且把傳入的數據賦值給data,左孩子和右孩子指針置為NULL,所以代碼如下:

//申請內存空間
BTNode* buyNode(BTDataType x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));node->data = x;node->left = node->right = NULL;
}

2.3手動構建一棵二叉樹

我們如果和我們之前的那張圖片一樣構造二叉樹的結果一樣的話,我們可以寫出以下代碼:

//手動構造一塊二叉樹
BTNode* CreateTree()
{BTNode* nodeA = buyNode('A');BTNode* nodeB = buyNode('B');BTNode* nodeC = buyNode('C');BTNode* nodeD = buyNode('D');BTNode* nodeE = buyNode('E');BTNode* nodeF = buyNode('F');nodeA->left = nodeB;nodeA->right = nodeC;nodeB->left = nodeD;nodeC->left = nodeE;nodeC->right = nodeF;return nodeA;
}

2.4二叉樹結點的個數

我們發現二叉樹結點個數等于左子樹的結點個數加上右結點個數加上根結點的個數(1)構成,而左子樹也等于左二叉樹的結點個數加上右二叉樹的結點個數加上根結點的個數(1)構成,所以最終代碼如下:

//二叉樹結點個數
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

2.5二叉樹葉子結點的個數

我們發現二叉樹葉子結點的個數=左子樹葉子結點的個數+右子樹葉子結點的個數,所以最終代碼如下:

//葉子結點的個數
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);
}

2.6二叉樹第K層結點個數

我們發現二叉樹第K層結點個數=左子樹第K層結點的個數+右子樹第K層結點的個數,所以最終代碼如下:

//二叉樹第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);
}

2.7二叉樹的高度

我們要比較的是左子樹深度和右子樹的深度,比較大的一方才算,即為根結點+max(左子樹,右子樹)我們先調用一下左子樹的遞歸序列,并把左子樹的深度存儲起來,然后把右子樹按照同樣的方法進行存儲,后面返回其中最大的一個加1即可,最終代碼如下:

//求二叉樹的深度
int BinaryTreeDepth(BTNode* root)
{if (root == NULL){return 0;}int leftDep = BinaryTreeDepth(root->left);int rightDep = BinaryTreeDepth(root->right);return 1 + (leftDep > rightDep ? leftDep : rightDep);
}

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

我們用前序遍歷方法進行查找,代碼如下:

//二叉樹中查找值為x的結點
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}BTNode* leftFind = BinaryTreeFind(root->left, x);if (leftFind){return leftFind;}BTNode* rightFind = BinaryTreeFind(root->right, x);if (rightFind){return rightFind;}return NULL;
}

2.9二叉樹的銷毀

我們需要傳一個指針,然后先進行左子樹的銷毀,最后再進行右子樹的銷毀,所以代碼如下:

//二叉樹的銷毀
void BinaryTreeDestory(BTNode** root)
{if (*root == NULL){return;}BinaryTreeDestory(&(*root)->left);//->的優先級大于*BinaryTreeDestory(&(*root)->right);free(*root);*root = NULL;
}

3.層序遍歷

3.1概念

層序遍歷就是從二叉樹的根結點出發,依次往左孩子到右孩子進行訪問,像我們上一副圖的層序遍歷就是ABCDEF。

3.2層序遍歷的實現

先說結論,我們用隊列來實現層序遍歷,先進行根結點入隊列,若發現不為空,則把隊頭數據進行出隊操作,若取出的結點有左孩子(右孩子),則把左孩子(右孩子)入隊列,依次類推。所以我們需要隊列這個數據結構的幫助。我們把之前的代碼復制過來有

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include<stdbool.h>
//定義隊列結點的結構
typedef int STDataType;
typedef struct QueueNode
{STDataType data;struct QueueNode* next;
}QueueNode;
//隊列的結構
typedef struct Queue
{QueueNode* phead;QueueNode* ptail;
}Queue;
//隊列的初始化
void QueueInit(Queue* pq)
{assert(pq);//防止傳參為空pq->phead = pq->ptail = NULL;
}
//入隊列
void QueuePush(Queue* pq, STDataType x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail!\n");exit(1);}newnode->data = x;newnode->next = NULL;if (pq->phead == NULL){//也可以寫為//pq->phead==pq->ptailpq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = newnode;//也可以把pq->ptail=newnode寫到if-else語句之后}
}
//隊列判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead == NULL;
}
//隊列有效元素的個數
int QueueSize(Queue* pq)
{int size = 0;QueueNode* pcur = pq->phead;while (pcur){size++;pcur = pcur->next;}return size;
}
//出隊列
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));if (pq->phead == pq->ptail){free(pq->phead);pq->phead = pq->ptail = NULL;}else{QueueNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}
}
//取隊頭數據
STDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}
//取隊尾數據
STDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->ptail->data;
}
//銷毀隊列
void QueueDestroy(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = pq->ptail = NULL;//可以加pq->size=0
}
//測試函數
void test()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);int size = QueueSize(&q);printf("%d\n", size);QueuePop(&q);size = QueueSize(&q);printf("%d\n", size);size = QueueFront(&q);printf("%d\n", size);size = QueueBack(&q);printf("%d\n", size);QueueDestroy(&q);
}
int main()
{test();return 0;
}

我們需要改以下的東西:typedef struct BiaryTreeNode*? STDataType;(我們所存儲的數據類型是struct BinaryTreeNode*),之所以是指針,因為我們之后要使用二級指針,這樣子更方便,所以最終實現有:

//層序遍歷
void LevelOrder(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* top = QueueFront(&q);QueuePop(&q);printf("%c ", top->data);if (top->left){QueuePush(&q, top->left);}if (top->right){QueuePush(&q, top->right);}}QueueDestroy(&q);
}

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

完全二叉樹有兩個特點:除最后一層外其余層數的結點個數都達到最大;最后一層結點從左到右依次排列。代碼實現如下(我會注釋):

//判斷是否為完全二叉樹
bool BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){//取隊頭出隊頭BTNode* top = QueueFront(&q);QueuePop(&q);if (top == NULL){break;}//把不為空的隊頭結點的左右孩子入隊列QueuePush(&q, top->left);QueuePush(&q, top->right);}//隊列不一定為空,繼續取隊頭出隊頭while (!QueueEmpty(&q)){BTNode* top = QueueFront(&q);QueuePop(&q);if (top != NULL){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}

5.總結

二叉樹鏈式代碼比較簡單,但是實現這個比較難。下節我將講解二叉樹的應用,喜歡的可以一鍵三連哦。

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

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

相關文章

鴻蒙HarmonyOS NEXT之無感監聽

鴻蒙中存在一些無感監聽&#xff0c;這些監聽經過系統API封裝使用很簡單&#xff0c;但是對實際業務開發中有很重要&#xff0c;例如埋點業務、數據統計、行為上報、切面攔截等。 Navigation的頁面切換 在鴻蒙中Navigation被用來作為路由棧進行頁面跳轉&#xff0c;如果你想知…

批量處理word里面表格的空白行

1&#xff0c;隨便打開一個word文檔。 2&#xff0c;按下Alt F11 VBA編輯器,在左側的「工程資源管理器」窗口中找到Normal 項目,右鍵選擇插入->模塊。 彈出一下彈窗 3&#xff0c;輸入一下代碼 代碼&#xff1a; Sub RemoveEmptyTableRows()Dim tbl As TableDim row As R…

3ds Max 2026 新功能全面解析

一、視口性能與交互體驗升級 1. Hydra 2.0 視口渲染引擎 3ds Max 2026 引入了 Hydra 2.0&#xff0c;大幅優化了視口渲染性能&#xff0c;尤其是在處理復雜場景和高質量實時預覽時&#xff0c;流暢度提升顯著。 支持USD&#xff08;通用場景描述&#xff09;格式&#xff0c…

JVM垃圾回收筆記02-垃圾回收器

文章目錄 前言1.串行(Serial 收集器/Serial Old 收集器)Serial 收集器Serial Old 收集器相關參數-XX:UseSerialGC 2.吞吐量優先(Parallel Scavenge 收集器/Parallel Old 收集器)Parallel Scavenge 收集器Parallel Old 收集器相關參數-XX:UseParallelGC ~ -XX:UseParallelOldGC-…

圖解AUTOSAR_SWS_UDPNetworkManagement

AUTOSAR UDP 網絡管理 (UdpNm) 技術詳解 基于 AUTOSAR 規范的 UDP 網絡管理模塊可視化指南 目錄 AUTOSAR UDP 網絡管理 (UdpNm) 技術詳解 目錄1. 概述2. UdpNm 狀態機 2.1 狀態機概述2.2 主要狀態說明2.3 狀態轉換機制2.4 并行狀態3. UdpNm 架構設計 3.1 架構概述3.2 接口設計3…

android 圖形開發的技能學習路線

需要以下幾個方面的知識&#xff1a; OpenGL ES的基礎和高級應用圖形渲染管線的工作原理3D數學&#xff08;矩陣、向量、四元數&#xff09;著色器編程&#xff08;GLSL&#xff09;libGDX框架的使用和定制性能優化和內存管理跨平臺渲染技術 接下來&#xff0c;考慮如何結構化…

使用AI一步一步實現若依(26)

功能26&#xff1a;新增一個新員工培訓頁面 功能25&#xff1a;角色管理 功能24&#xff1a;菜單管理 功能23&#xff1a;從后端獲取路由/菜單數據 功能22&#xff1a;用戶管理 功能21&#xff1a;使用axios發送請求 功能20&#xff1a;使用分頁插件 功能19&#xff1a;集成My…

vue響應式原理剖析

一、什么是響應式? 我們先來看一下響應式意味著什么?我們來看一段代碼: m有一個初始化的值,有一段代碼使用了這個值; 那么在m有一個新的值時,這段代碼可以自動重新執行; let m = 20 console.log(m) console.log(m * 2)m = 40上面的這樣一種可以自動響應數據變量的代碼機…

無人機航電系統電池技術解析!

1. 常用電池類型 鋰聚合物電池&#xff08;LiPo&#xff09; 特點&#xff1a;高能量密度、輕量化、放電效率高&#xff0c;是目前主流選擇。 缺點&#xff1a;對過充/過放敏感&#xff0c;需嚴格管理&#xff0c;存在輕微膨脹或起火風險。 鋰離子電池&#xff08;Li-ion…

ubuntu下終端打不開的排查思路和解決方法

問題現象描述&#xff1a;ubuntu開機后系統桌面顯示正常&#xff0c;其他圖形化的app也都能打開無異常&#xff0c;唯獨只有terminal終端打不開&#xff0c;無論是鼠標點擊終端軟件&#xff0c;還是ctrlaltt&#xff0c;還是altF2后輸入gnome-terminal后按回車&#xff0c;這三…

Maven入門

1、簡介 Apache Maven是一個項目管理及自動構建工具&#xff0c;由Apache軟件基金會所提供。基于項目對象模型&#xff08;縮寫&#xff1a;POM&#xff09;概念&#xff0c;Maven利用一個中央信息片斷能管理一個項目的構建、報告和文檔等步驟。 2、作用 1&#xff09;依賴導…

Rk3588,Opencv讀取Gmsl相機,Rga yuv422轉換rgb (降低CPU使用率)

RK3588, 使用OpenCv 讀取 gmsl 相機,獲得yuv422格式圖像, 使用 rga 轉換 rgb 圖像。減少cpu占用率. 查看相機信息 v4l2-ctl --all -d /dev/cam0 , 查看自己相機分辨率,輸出格式等信息,對應修改后續代碼測試… Driver Info:Driver name : rkcifCard type : rkc…

鴻蒙相機開發實戰:從設備適配到性能調優 —— 我的 ArkTS 錄像功能落地手記(API 15)

引言&#xff1a;為什么我要寫這份開發指南&#xff1f; 作為一名老技術&#xff0c;最近特別喜歡研究鴻蒙相機功能&#xff0c;而且目前已經更新到API15了&#xff0c;那么咱們更要好好研究一下。而且從手持云臺到車載記錄儀&#xff0c;每個項目都面臨獨特挑戰&#xff1a;車…

【NLP 49、提示工程 prompt engineering】

目錄 一、基本介紹 語言模型生成文本的基本特點 提示工程 prompt engineering 提示工程的優勢 使用注意事項 ① 安全問題 ② 可信度問題 ③ 時效性與專業性 二、應用場景 能 ≠ 適合 應用場景 —— 百科知識 應用場景 —— 寫文案 應用場景 —— 解釋 / 編寫…

數字轉換(c++)

【題目描述】 如果一個數 xx 的約數和 yy &#xff08;不包括他本身&#xff09;比他本身小&#xff0c;那么 xx 可以變成 yy &#xff0c;yy 也可以變成 xx 。例如 44 可以變為 33 &#xff0c;11 可以變為 77 。限定所有數字變換在不超過 nn 的正整數范圍內進行&#xff0c;…

如何同步fork的更新

當你fork了一個代碼倉庫后&#xff0c;要將其與原始源碼保持同步&#xff0c;可以按照以下步驟進行操作&#xff1a; 1. 添加原始倉庫作為遠程源 在本地命令行中&#xff0c;進入到你fork后的代碼倉庫目錄&#xff0c;然后使用以下命令添加原始倉庫&#xff08;通常稱為upstr…

CentOS系統下安裝tesseract-ocr5.x版本

CentOS系統下安裝tesseract-ocr5.x版本 安裝依賴包&#xff1a; yum update -y yum install autoconf automake libtool libjpeg-devel libpng-devel libtiff-devel zlib-devel yum install automake libtool bzip2 -y手動編譯安裝GCC&#xff08;因系統默認安裝的GCC版本比較…

MyBatis打印SQL日志的配置

配置MyBatis打印日志的步驟如下&#xff0c;支持多種日志框架&#xff08;如Logback、Log4j2等&#xff09;&#xff1a; 一、選擇日志框架并添加依賴&#xff08;以常見組合為例&#xff09; 1. Logback&#xff08;推薦&#xff09; <!-- Maven 依賴 --> <depende…

SpringCould微服務架構之Docker(3)

1&#xff09;什么是鏡像和容器&#xff1f; 2&#xff09;DockerHub&#xff1a; 3&#xff09;docker的架構如下&#xff1a;

智慧高速,安全護航:視頻監控平臺助力高速公路高效運營

隨著我國高速公路里程的不斷增長&#xff0c;交通安全和運營效率面臨著前所未有的挑戰。傳統的監控方式已難以滿足現代化高速公路管理的需求&#xff0c;而監控視頻平臺的出現&#xff0c;則為高速公路的安全運營提供了強有力的技術支撐。高速公路視頻監控聯網解決方案 高速公路…