【數據結構】09.樹與二叉樹

一、樹的概念與結構

1.1 樹的概念

樹是一種非線性的數據結構,它是由n(n>=0)個有限結點組成一個具有層次關系的集合。把它叫做樹是因為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。

  • 根結點:根節點沒有前驅結點。
  • 除根節點外,其余結點被分成是一棵結構與樹類似的子樹。每棵子樹的根結點有且只有一個前驅,可以有0個或多個后繼。
  • 因此,樹是遞歸定義的。

在這里插入圖片描述

1.2 樹的相關概念

在這里插入圖片描述

  • 節點的度:一個節點含有的子樹的個數稱為該節點的度; 如上圖:A的為4
  • 葉節點或終端節點:度為0的節點稱為葉節點; 如上圖:B、F、G、D、H節點為葉節點
  • 非終端節點或分支節點:度不為0的節點; 如上圖:A、C、E節點為分支節點
  • 雙親節點或父節點:若一個節點含有子節點,則這個節點稱為其子節點的父節點; 如上圖:A是B的父節點
  • 孩子節點或子節點:一個節點含有的子樹的根節點稱為該節點的子節點; 如上圖:B是A的孩子節點
  • 兄弟節點:具有相同父節點的節點互稱為兄弟節點; 如上圖:B、C是兄弟節點
  • 樹的度:一棵樹中,最大的節點的度稱為樹的度; 如上圖:樹的度為4
  • 節點的層次:從根開始定義起,根為第1層,根的子節點為第2層,以此類推;
  • 樹的高度或深度:樹中節點的最大層次; 如上圖:樹的高度為3
  • 堂兄弟節點:雙親在同一層的節點互為堂兄弟;如上圖:F、G互為兄弟節點
  • 節點的祖先:從根到該節點所經分支上的所有節點;如上圖:A是所有節點的祖先
  • 子孫:以某節點為根的子樹中任一節點都稱為該節點的子孫。如上圖:所有節點都是A的子孫
  • 森林:由m棵互不相交的樹的集合稱為森林;

1.3 樹的表示法

樹結構相對線性表就比較復雜了,要存儲表示起來就比較麻煩了,既要保存值域,也要保存結點和結點之間的關系,實際中樹有很多種表示方式如:雙親表示法,孩子表示法、孩子雙親表示法以及孩子兄弟表示法等。

在介紹以下存儲結構的過程中,我們都以下面這個樹為例子。在這里插入圖片描述

1.3.1 雙親表示法

我們假設以一組連續空間存儲樹的結點,同時在每個結點中,附設一個指示器指示其雙親結點到鏈表中的位置。也就是說,每個結點除了知道自已是誰以外,還知道它的雙親在哪里。
在這里插入圖片描述
其中data是數據域,存儲結點的數據信息。而parent是指針域,存儲該結點的雙親在數組中的下標。
以下是我們的雙親表示法的結點結構定義代碼。

/*樹的雙親表示法結點結構定義*/
#define MAX_TREE_SIZE 100
typedef int TElemType;	//樹結點的數據類型,目前暫定為整型
/*結點結構*/
typedef struct TreeNode
{TElemType data;	//結點數據int parent;	//雙親位置
}TreeNode;
/*樹結構*/
typedef struct
{TreeNode nodes[MAX_TREE_SIZE];	//結點數組int r, n;	//根的位置和結點數
}PTree;

這樣的存儲結構,我們可以根據結點的parent 指針很容易找到它的雙親結點,所用的時間復雜度為0(1),直到parent為-1時,表示找到了樹結點的根。可如果我們要知道結點的孩子是什么,對不起,請遍歷整個結構才行。

1.3.2 孩子兄弟表示法

剛才我們分別從雙親的角度和從孩子的角度研究樹的存儲結構,如果我們從樹結點的兄弟的角度又會如何呢?當然,對于樹這樣的層級結構來說,只研究結點的兄弟是不行的,我們觀察后發現,任意一棵樹, 它的結點的第一個孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。 因此,我們設置兩個指針,分別指向該結點的第一個孩子和此結點的右兄弟。

在這里插入圖片描述

/*樹的孩子兄弟表示法結構定義*/
typedef struct TreeNode
{TElemtype data;struct TreeNode *firstchild, *rightsib;
} TreeNode,* pTreeNode;

1.4 樹在實際中的應用

在這里插入圖片描述

二、二叉樹的概念及結構

2.1概念

一棵二叉樹是結點的一個有限集合,該集合:

  • 或者為空
  • 由一個根節點加上兩棵別稱為左子樹和右子樹的二叉樹組成

在這里插入圖片描述

從上圖可以看出:

  1. 二叉樹不存在度大于2的結點
  2. 二叉樹的子樹有左右之分,次序不能顛倒,因此二叉樹是有序樹

注意:對于任意的二叉樹都是由以下幾種情況復合而成的:
在這里插入圖片描述

2.2 特殊的二叉樹

  1. 滿二叉樹:一個二叉樹,如果每一個層的結點數都達到最大值,則這個二叉樹就是滿二叉樹。也就是說,如果一個二叉樹的層數為K,且結點總數是2^k-1 ,則它就是滿二叉樹。
  2. 完全二叉樹:完全二叉樹是效率很高的數據結構,完全二叉樹是由滿二叉樹而引出來的。對于深度為K的,有n個結點的二叉樹,當且僅當其每一個結點都與深度為K的滿二叉樹中編號從1至n的結點一一對應時稱之為完全二叉樹。 要注意的是滿二叉樹是一種特殊的完全二叉樹。
    在這里插入圖片描述

2.3 二叉樹的性質

  1. 若規定根節點的層數為1,則一棵非空二叉樹的第i層上最多有2^(i-1) 個結點
  2. 若規定根節點的層數為1,則深度為h的二叉樹的最大結點數是 2^h-1
  3. 對任何一棵二叉樹, 如果度為0其葉結點個數為n0 , 度為2的分支結點個數為 n2,則有 n0=n2 +1
  4. 若規定根節點的層數為1,具有n個結點的滿二叉樹的深度,h=log (n+1) (ps: 是log以2為底,n+1為對數)
  5. 對于具有n個結點的完全二叉樹,如果按照從上至下從左至右的數組順序對所有節點從0開始編號,則對于序號為i的結點有:
    • 若i>0,i位置節點的雙親序號:(i-1)/2;i=0,i為根節點編號,無雙親節點
    • 若2i+1<n,左孩子序號:2i+1,2i+1>=n否則無左孩子
    • 若2i+2<n,右孩子序號:2i+2,2i+2>=n否則無右孩子

2.4 二叉樹的存儲結構

二叉樹一般可以使用兩種結構存儲,一種順序結構,一種鏈式結構。

  1. 順序存儲
    順序結構存儲就是使用數組來存儲,一般使用數組只適合表示完全二叉樹,因為不是完全二叉樹會有空間的浪費。而現實中使用中只有堆才會使用數組來存儲。二叉樹順序存儲在物理上是一個數組,在邏輯上是一顆二叉樹。
    在這里插入圖片描述2. 鏈式存儲
    二叉樹的鏈式存儲結構是指,用鏈表來表示一棵二叉樹,即用鏈來指示元素的邏輯關系。 **通常的方法是鏈表中每個結點由三個域組成,數據域和左右指針域,左右指針分別用來給出該結點左孩子和右孩子所在的鏈結點的存儲地址 。**鏈式結構又分為二叉鏈和三叉鏈,當前我們學習中一般都是二叉鏈。
    在這里插入圖片描述

三、二叉樹的順序結構及其實現

普通的二叉樹是不適合用數組來存儲的,因為可能會存在大量的空間浪費。而完全二叉樹更適合使用順序結構存儲。現實中我們通常把堆(一種二叉樹)使用順序結構的數組來存儲,需要注意的是這里的堆和操作系統虛擬進程地址空間中的堆是兩回事,一個是數據結構,一個是操作系統中管理內存的一塊區域分段。
具體的實現及應用請查看:【數據結構】08.堆及堆的應用

四、二叉樹的鏈式結構及其實現

4.1 二叉樹的遍歷

學習二叉樹結構,最簡單的方式就是遍歷。所謂二叉樹遍歷(Traversal)是按照某種特定的規則,依次對二叉樹中的節點進行相應的操作,并且每個節點只操作一次。訪問結點所做的操作依賴于具體的應用問題。 遍歷是二叉樹上最重要的運算之一,也是二叉樹上進行其它運算的基礎。

4.1.1 前序遍歷

//根 左子樹 右子樹
void PrevOrder(pTreeNode root)
{if (root == NULL){printf("NULL ");return;}printf("%d ", root->val);PrevOrder(root->left);PrevOrder(root->right);
}

下圖為遞歸過程:
在這里插入圖片描述

4.1.2 中序遍歷

//左子樹 根 右子樹
void InOrder(pTreeNode root)
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%d ", root->val);InOrder(root->right);
}

下面為遞歸過程:
在這里插入圖片描述

4.1.3 后序遍歷

//左子樹 右子樹 根
void PostOrder(pTreeNode root)
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%d ", root->val);
}

下面為遞歸過程:
在這里插入圖片描述

4.2.4 層序遍歷

層序遍歷:除了先序遍歷、中序遍歷、后序遍歷外,還可以對二叉樹進行層序遍歷。設二叉樹的根節點所在層數為1,層序遍歷就是從所在二叉樹的根節點出發,首先訪問第一層的樹根節點,然后從左到右訪問第2層上的節點,接著是第三層的節點,以此類推,自上而下,自左至右逐層訪問樹的結點的過程就是層序遍歷
圖示:
在這里插入圖片描述

我們這里介入隊列來進行二叉樹的前序遍歷。

// 層序遍歷
void LevelOrder(pTreeNode root)
{Queue q;QueueInit(&q);if (root){QueuePush(&q, root);}while (!QueueEmpty(&q)){pTreeNode front = QueueFront(&q);QueuePop(&q);if (front == NULL){printf("NULL ");}else{printf("%d ", front->val);QueuePush(&q, front->left);QueuePush(&q, front->right);}}QueueDestory(&q);
}

4.1 二叉樹的創建與銷毀

二叉樹的創建與銷毀,我們以二叉樹遍歷為例題。

//二叉樹的創建
struct TreeNode* Creat(char* arr,int n,int* i)
{ if(*i<n&&arr[*i]=='#'){(*i)++;return NULL;}TreeNode* newnode=(TreeNode*)malloc(sizeof(TreeNode));newnode->left=NULL;newnode->right=NULL;newnode->val=arr[(*i)++];newnode->left=Creat(arr,n,i);newnode->right=Creat(arr,n,i);return newnode;
}
//二叉樹的銷毀
void TreeDestroy(struct TreeNode* root)
{if(root==NULL){return;}TreeDestroy(root->left);TreeDestroy(root->right);free(root);
}

4.3 二叉樹的其他操作

以下的操作均是沿著遍歷的思想展開的。

// 二叉樹節點個數
int TreeSize(pTreeNode root)
{if (root == NULL){return 0;}return TreeSize(root->left) + TreeSize(root->right) + 1;
}
// 二叉樹葉子節點個數
int TreeLeafSize(pTreeNode root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}
// 二叉樹第k層節點個數
int TreeLevelKSize(pTreeNode root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}return TreeLevelKSize(root->left, k - 1) + TreeLevelKSize(root->right, k - 1);
}
// 二叉樹查找值為x的節點
pTreeNode TreeFind(pTreeNode root, TreeDataType x)
{if (root == NULL){return NULL;}//相等就返回if (root->val == x)return root;//找左子樹pTreeNode left=TreeFind(root->left, x);if (left){return left;}//找右子樹pTreeNode right = TreeFind(root->right, x);if (right){return right;}//都沒找到return NULL;
}
//二叉樹的高度
int TreeHeight(pTreeNode root)
{if (root == NULL){return 0;}int max_left = TreeHeight(root->left) ;int max_right = TreeHeight(root->right);return max_left > max_right ? max_left + 1 : max_right + 1;
}
//判斷是否是完全二叉樹
bool TreeComplete(pTreeNode root)
{Queue q;QueueInit(&q);if (root){QueuePush(&q, root);}while (!QueueEmpty(&q)){pTreeNode front = QueueFront(&q);QueuePop(&q);if (front == NULL){break;}else{QueuePush(&q, front->left);QueuePush(&q, front->right);}}while (!QueueEmpty(&q)){pTreeNode front = QueueFront(&q);QueuePop(&q);if (front){QueueDestory(&q);return false;}}QueueDestory(&q);return true;
}

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

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

相關文章

04采訪:數字人直播

?AI技術的迭代對數字人直播一定是有正向推動作用的。直播可持續性差,投入產出極不協調。不適合前期大量投入。直播現在這個東西有一個問題,因為直播開始帶貨了,就已經不是一個單純的娛樂性質的視頻內容,而是對帶有一種商業目的內容。 直播帶貨的痛點:對主播而言是觀眾;…

俯臥撐計數器(Python)

通過 MediaPipe 檢測人體姿態&#xff0c;計算俯臥撐角度和計數&#xff0c;并在圖像上進行可視化展示 需要有cv2庫和mediapipe庫 mediapipe庫&#xff1a; MediaPipe是Google開源的機器學習框架&#xff0c;用于構建實時音頻、視頻和多媒體處理應用程序。它提供了一組預訓練的…

一文清晰了解HTML

有這樣一個txt記事本文件和一張圖片&#xff1a; txt文本內容是這樣的&#xff1a; <html><head><title>HTML學習</title></head><body><h1>hello HTML</h1><img src"高清修復.png"/></body> </html…

LabVIEW的JKI State Machine

JKI State Machine是一種廣泛使用的LabVIEW架構&#xff0c;由JKI公司開發。這種狀態機架構在LabVIEW中提供了靈活、可擴展和高效的編程模式&#xff0c;適用于各種復雜的應用場景。JKI State Machine通過狀態的定義和切換&#xff0c;實現了程序邏輯的清晰組織和管理&#xff…

VSCode工程中task.json的作用

在 Visual Studio Code&#xff08;VSCode&#xff09;中&#xff0c;tasks.json 文件是用來定義和配置任務&#xff08;Tasks&#xff09;的。任務指的是在開發過程中需要自動化執行的一系列操作&#xff0c;例如編譯代碼、運行測試、打包項目等。通過配置 tasks.json&#xf…

In Search of Lost Online Test-time Adaptation: A Survey--論文筆記

論文筆記 資料 1.代碼地址 https://github.com/jo-wang/otta_vit_survey 2.論文地址 https://arxiv.org/abs/2310.20199 3.數據集地址 1論文摘要的翻譯 本文介紹了在線測試時間適應(online test-time adaptation,OTTA)的全面調查&#xff0c;OTTA是一種專注于使機器學習…

【軟件分享】我們都需要會用的ArcGIS10.8和ArcGIS Pro

ArcGIS是地理人必備的地理制圖、空間分析常用的工具&#xff0c;讀地理&#xff0c;或多或少都會接觸到ArcGIS的使用&#xff0c;今天小編要帶來的就是ArcGIS10.8軟件資源和升級版ArcGIS Pro的軟件資源。 軟件安裝包獲取 公眾號回復關鍵詞&#xff1a;“ArcGIS"&#xff…

*算法訓練(leetcode)第二十五天 | 134. 加油站、135. 分發糖果、860. 檸檬水找零、406. 根據身高重建隊列

刷題記錄 134. 加油站135. 分發糖果860. 檸檬水找零406. 根據身高重建隊列 134. 加油站 leetcode題目地址 記錄全局剩余油量和當前剩余油量&#xff0c;當前剩余小于0時&#xff0c;其實位置是當前位置的后一個位置。若全局剩余油量為負&#xff0c;則說明整體油量不足以走完…

防爆手機終端安全管理平臺

防爆手機終端安全管理平臺能夠滿足國家能源、化工企業對安全生產信息化運行需求&#xff0c;能夠快速搭建起高效、快捷的移動終端管理平臺&#xff0c;提高企業安全生產管理水平&#xff0c;保證企業的安全運行和可持續發展。#防爆手機 #終端安全 #移動安全 能源、化工等生產單…

公有鏈、私有鏈與聯盟鏈:區塊鏈技術的多元化應用與比較

引言 區塊鏈技術自2008年比特幣白皮書發布以來&#xff0c;迅速發展成為一項具有顛覆性潛力的技術。區塊鏈通過去中心化、不可篡改和透明的方式&#xff0c;提供了一種全新的數據存儲和管理方式。起初&#xff0c;區塊鏈主要應用于加密貨幣&#xff0c;如比特幣和以太坊。然而&…

SQL Server 設置端口詳解

前言 在數據庫管理和開發過程中&#xff0c;SQL Server是一個廣泛使用的關系型數據庫管理系統。默認情況下&#xff0c;SQL Server使用1433端口進行通信。然而&#xff0c;出于安全性、端口沖突或網絡限制等原因&#xff0c;我們有時需要更改SQL Server的默認端口。本文將詳細…

VBA-計時器的數據進行整理

對計時器的數據進行整理 需求原始數據程序步驟VBA程序結果 需求 需要在txt文件中提取出分和秒分別在兩列 原始數據 數據結構 計次7 00:01.855 計次6 00:09.028 計次5 00:08.586 計次4 00:08.865 計次3 00:07.371 計次2 00:06.192 計次1 00:05.949 程序步驟 1、利用Trim()去…

易備數據備份軟件——低成本、高效能、全方位地守護您的數據安全

在數字化的時代&#xff0c;數據是企業和個人最寶貴的資產。然而&#xff0c;數據丟失、系統故障、惡意攻擊等威脅時刻存在。如何確保數據的安全與完整&#xff1f;易備數據備份軟件為您提供全方位無死角的解決方案&#xff0c;讓您高枕無憂&#xff01; 云備份&#xff1a;暢…

CV每日論文--2024.7.4

1、InternLM-XComposer-2.5: A Versatile Large Vision Language Model Supporting Long-Contextual Input and Output 中文標題&#xff1a;InternLM-XComposer-2.5&#xff1a;支持長上下文輸入和輸出的多功能大視覺語言模型 簡介&#xff1a;我們推出了InternLM-XComposer-…

079、類的繼承

繼承是對已有的類進行擴展創建出新的類&#xff0c;這個過程就叫做繼承。其中&#xff0c;提供繼承信息的類叫做父類&#xff08;超類、基類&#xff09;&#xff0c;得到繼承信息的類稱為子類&#xff08;派生類&#xff09;。 基本語法 繼承是通過在類定義語句中使用圓括號…

控制周期與控制頻率

控制周期是指控制系統中執行一次完整控制循環所需的時間間隔。它表示了控制系統對輸入信號進行處理、執行控制算法、生成輸出信號并更新系統狀態的頻率。在實時控制系統中&#xff0c;控制周期的選擇對系統的性能和穩定性具有重要影響。較短的控制周期可以提高系統的響應速度&a…

高級java每日一道面試題-2024年7月8日

文章目錄 面試官問: final 在java中有什么作用面試者回答:1. final修飾變量基本數據類型&#xff1a;示例&#xff1a; 對象引用&#xff1a;示例&#xff1a; 2. final修飾方法示例&#xff1a; 3. final修飾類示例&#xff1a; 4. final局部變量和參數示例&#xff1a; 總結 …

互聯網十萬個為什么之什么是CDN?

CDN&#xff08;Content Delivery Network&#xff0c;內容分發網絡&#xff09;是一組分布在不同地理位置的服務器&#xff0c;其目的是更有效地向用戶分發互聯網內容。通過緩存內容&#xff08;如網頁、圖片、視頻和其他類型的網絡數據&#xff09;在多個服務器上&#xff0c…

學生護眼臺燈哪個牌子實用?值得入手的學生護眼臺燈十大排名分析

在這個數碼時代&#xff0c;人們對屏幕的依賴程度越來越高&#xff0c;尤其是孩子們。他們不僅在學校里需要長時間盯著教科書&#xff0c;還會在學習和娛樂中使用各種數碼設備。然而&#xff0c;這也使得眼睛健康問題逐漸凸顯&#xff0c;尤其是兒童近視的問題。為了保護視力&a…

Flink 提交作業的方式

參考&#xff1a; Flink運行方式及對比-騰訊云開發者社區-騰訊云