數據結構--二叉樹

目錄

?1.二叉樹鏈式結構的實現

1.1 前置說明

1.2?二叉樹的遍歷

1.2.1 前序、中序以及后序遍歷

1.2.2?層序遍歷及判斷是否為完全二叉樹

1.3?節點個數,葉子節點個數,第k層節點個數以及高度等

1.4 二叉樹的創建和銷毀


?1.二叉樹鏈式結構的實現


1.1 前置說明

? ? ? ? 這時直接創建的二叉樹,方便于各個接口函數的測試,當然你也可以選擇1.4的方法直接創建。

typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}TreeNode;TreeNode* BuyTreeNode(int x)
{TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));assert(node);node->data = x;node->left = NULL;node->right = NULL;return node;
}
TreeNode* CreateTree()
{TreeNode* node1 = BuyTreeNode(1);TreeNode* node2 = BuyTreeNode(2);TreeNode* node3 = BuyTreeNode(3);TreeNode* node4 = BuyTreeNode(4);TreeNode* node5 = BuyTreeNode(5);TreeNode* node6 = BuyTreeNode(6);TreeNode* node7 = BuyTreeNode(7);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;node5->right = node7;return node1;
}

再看二叉樹基本操作前,再回顧下二叉樹的概念,二叉樹是:
????????1. 空樹
????????2. 非空:根節點,根節點的左子樹、根節點的右子樹組成的。

????????從概念中可以看出,二叉樹定義是遞歸式的,因此后序基本操作中基本都是按照該概念實現的。


1.2?二叉樹的遍歷


1.2.1 前序、中序以及后序遍歷

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

按照規則,二叉樹的遍歷有:前序/中序/后序的遞歸結構遍歷:
????????1. 前序遍歷(Preorder Traversal 亦稱先序遍歷)——訪問根結點的操作發生在遍歷其左右子樹之前。
????????2. 中序遍歷(Inorder Traversal)——訪問根結點的操作發生在遍歷其左右子樹之中間。
????????3. 后序遍歷(Postorder Traversal)——訪問根結點的操作發生在遍歷其左右子樹之后。

下面主要分析前序遞歸遍歷,中序與后序圖解類似,可以自己畫畫看。

????????(1.)**前序遍歷**,也稱為**深度優先遍歷**。它從根節點開始,遞歸地訪問左子樹,然后訪問右子樹。 二叉樹的前序遍歷是按以下順序訪問節點的序列: 1. 根節點 2. 根節點的左子樹 3. 根節點的右子樹

????????當左邊遞歸到空時,會從葉子節點開始依次返回遞歸的結果,然后開始遍歷右子樹,遞歸迭代。
前序遍歷遞歸圖解:

前序遍歷的代碼:

void PrevOrder(TreeNode* root)
{if (root == NULL){printf("N ");return;}printf("%d ", root->data);PrevOrder(root->left);PrevOrder(root->right);
}

前序遍歷結果為:1 2 3 4 5 6

????????(2.)**中序遍歷**。它從根節點開始,遞歸地訪問左子樹,然后訪問當前節點,然后訪問右子樹。 二叉樹的中序遍歷是按以下順序訪問節點的序列: 1. 當前節點的左子樹 2. 當前節點 3. 當前節點的右子樹

中序遍歷代碼:

void InOrder(TreeNode* root)
{if (root == NULL){printf("N ");return;}InOrder(root->left);printf("%d ", root->data);InOrder(root->right);
}

中序遍歷的結果:3 2 1 5 4 6

????????(3.)**后序遍歷**。它從根節點開始,遞歸地訪問左子樹,然后訪問右子樹,最后訪問根節點。 二叉樹的后序遍歷是按以下順序訪問節點的序列: 1. 左子樹的所有節點 2. 右子樹的所有節點 3. 根節點

后序遍歷代碼:

void PostOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}PrevOrder(root->left);PrevOrder(root->right);printf("%d ", root->data);
}

前序遍歷結果為:3 2 5 6 4 1


1.2.2?層序遍歷及判斷是否為完全二叉樹

層序遍歷(又稱廣度優先遍歷):除了先序遍歷、中序遍歷、后序遍歷外,還可以對二叉樹進行層序遍歷。設二叉樹的根節點所在層數為1,層序遍歷就是從所在二叉樹的根節點出發,首先訪問第一層的樹根節點,然后從左到右訪問第2層上的節點,接著是第三層的節點,以此類推,自上而下,自左至右逐層訪問樹的結點的過程就是層序遍歷。

????????實現層序遍歷,我們可以用到隊列,A進入隊列,可以去到B和C的地址,讓A出隊就能取到A的數據,然后通過B可以取到D、通過C可以取到E,F,讓他們依次入隊出隊,就能取到每一層 的節點,最后隊列為空就結束

void LevelOrder(TreeNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);int levelSize = 1;while (!QueueEmpty(&q)){// 一層一層出while (levelSize--){TreeNode* front = QueueFront(&q);QueuePop(&q);//這里pop掉了front也能取到,因為這只是pop掉了指向節點的指針//并沒有真的把節點pop掉了printf("%d ", front->data);if (front->left)QueuePush(&q, front->left);if (front->right)QueuePush(&q, front->right);}printf("\n");levelSize = QueueSize(&q);}printf("\n");QueueDestroy(&q);
}

判斷是否為完全二叉樹

? ? ? ? 在層序遍歷的基礎上,我們稍作修改就可以了。我們再出隊列的過程中,如果遇到了NULL,那我們就break,然后去判斷NULL的后面是否有數據,如果NULL的后面還有數據,那么這就不是一個完全二叉樹。

bool TreeComplete(TreeNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);int levelSize = 1;while (!QueueEmpty(&q)){TreeNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL)break;QueuePush(&q, front->left);QueuePush(&q, front->right);}// 前面遇到空以后,后面還有非空就不是完全二叉樹while (!QueueEmpty(&q)){TreeNode* front = QueueFront(&q);QueuePop(&q);if (front)//判斷是否為NULL,不是NULL返回false{QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}

1.3?節點個數,葉子節點個數,第k層節點個數以及高度等

接口函數

// 二叉樹節點個數
int BinaryTreeSize(BTNode* root);
// 二叉樹葉子節點個數
int BinaryTreeLeafSize(BTNode* root);
// 二叉樹第k層節點個數
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉樹查找值為x的節點
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

1.二叉樹節點的個數

? ? ? ? 我們用分治的思想,依次遍歷左右兩子樹,如果不是空則+1

int TreeSize(TreeNode* root)
{return root == NULL ? 0 : TreeSize(root->left) +TreeSize(root->right) + 1;
}

2.葉子節點的個數

? ? ? ? 葉子節點的特征就是,左孩子右孩子都為空,則視為葉子節點,分別遍歷兩個子樹的葉子節點,是葉子節點就返回1。

int treeleafsize(TreeNode* root)
{//空返回0if (root == NULL)return 0;//是葉子返回1if ((root->left == NULL) &&(root->right == NULL))return 1;//非0也不是葉子,那繼續往下找葉子//分治 葉子=左+右return treeleafsize(root->left) +treeleafsize(root->right);
}

3.第k層的節點個數

? ? ? ? 同樣的,這里我們也用分治的思想。我們引入了變量k,我們從第一層開始,如果k不等于1的話,我就進行k-1的操作,直到k=1就到達了指定層數,k=1那么節點數就+1,統計每次遞歸到k=1的節點,就得到了第k層的節點數。

int lvksize(TreeNode* root, int k)
{assert(k > 0);if (root == NULL)return 0;if (k == 1){return 1;}return lvksize(root->left, k - 1) + lvksize(root->right, k - 1);
}

4.二叉樹查找值為x的節點

? ? ? ? 節點的查找不再是簡單的遍歷,我們遞歸一次就要保存一次遞歸的節點,因為遞歸是返回給每次調用的函數本身,函數是不能存值的,因此我們需要一個變量保存。

TreeNode* findtree(TreeNode* root, int x)
{if (root == NULL){return NULL;}if (root->data==x){return root;}//保存左子樹的結果TreeNode* ret=findtree(root->left,x);if (ret){return ret;} //保存右子樹的結果TreeNode* ret1=findtree(root->right, x);if (ret1){return ret1;}
}

5. 二叉樹的高度

要找到二叉樹的高度,我們可以使用以下算法(同樣是分治的思想):

????????1. 從根節點開始。

????????2. 如果節點沒有子節點,那么它的高度為 0。

? ? ? ? 3.分別遍歷左右子樹

? ? ? ? 4. 如果節點有一個子節點,那么它的高度為 1 加上其子節點的高度。

? ? ? ? 5. 如果節點有兩個子節點,那么它的高度為 1 加上其子節點高度的最大值。

? ? ? ? 6.比較左右子樹的大小,大的那個為二叉樹的高度,最后加上根節點,就得到了二叉樹的高度。

int TreeHeight(TreeNode* root)
{if (root == NULL)return 0;return fmax(TreeHeight(root->left), TreeHeight(root->right)) + 1;
}

這里我們用到fmax函數進行比較,當然你也可以選擇使用運算符進行比較。


1.4 二叉樹的創建和銷毀

1.二叉樹的創建

? ? ? ? 用先序,中序,后序的方式去直接創建二叉樹,那么,知道先序的序列就用先序的序列去遞歸創建樹,知道中序的序列就用中序的序列去遞歸創建樹,知道后序的序列就用后序的序列去遞歸創建樹

eg:用數組來存儲

TreeNode* TreeCreate(char* a, int* pi)
{if (a[*pi] == '#'){(*pi)++;return NULL;}TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));if (root == NULL){perror("malloc fail");exit(-1);}root->data = a[(*pi)++];root->left = TreeCreate(a, pi);root->right = TreeCreate(a, pi);return root;
}

2.二叉樹的銷毀

? ? ? ? 關于二叉樹的銷毀,你可以以任意序列的去銷毀二叉樹

void destroy_tree(Node *root) {if (root == NULL) {return;}destroy_tree(root->left);destroy_tree(root->right);free(root);
}

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

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

相關文章

Mysql啟動占用內存過高解決

Hi, I’m Shendi Mysql啟動占用內存過高解決 前言 最近服務器內存不夠用了,甚至還出現了內存溢出問題,導致程序宕機。但請求與用戶量并沒有多少,所以從各種啟動的程序中想方設法的盡可能的減少其占用的內存。 而在我的服務器中,…

幾何尺寸智能測量儀為您帶來經濟效益提升

線材、棒材、管材、板材等產品的品質檢測離不開一些基礎幾何尺寸的檢測,隨著產線自動化的提升,越來越多的產線開始使用智能測量儀,這不僅僅是因為其能帶來品質的提升,更是因為其能帶來的經濟效益。 幾何尺寸智能測量儀種類繁多&am…

JAVA網絡編程——BIO、NIO、AIO深度解析

I/O 一直是很多Java同學難以理解的一個知識點,這篇帖子將會從底層原理上帶你理解I/O,讓你看清I/O相關問題的本質。 1、I/O的概念 I/O 的全稱是Input/Output。雖常談及I/O,但想必你也一時不能給出一個完整的定義。搜索了谷哥欠,發…

區塊鏈創新應用場景不斷拓展,實現去中心化

小編介紹:10年專注商業模式設計及軟件開發,擅長企業生態商業模式,商業零售會員增長裂變模式策劃、商業閉環模式設計及方案落地;扶持10余個電商平臺做到營收過千萬,數百個平臺達到百萬會員,歡迎咨詢。 區塊…

【Vulnhub 靶場】【BuffEMR: 1.0.1】【簡單 - 中等】【20210831】

1、環境介紹 靶場介紹:https://www.vulnhub.com/entry/buffemr-101,717/ 靶場下載:https://download.vulnhub.com/buffemr/BuffEMR-v1.0.1.ova 靶場難度:簡單 - 中等 發布日期:2021年08月31日 文件大小:4.6 GB 靶場作…

為什么每個 Java 開發者都需要了解 Scala

前面我們一起回顧了第九期 Scala & Java Meetup 中最受關注的話題 —— jdk 并發編程的終極解決方案:虛擬線程,探討了這一新特性對包括 Scala 在內的響應式編程語言的影響。 本次 Meetup 的首位分享者 Chunsen,在加入 Tubi 成為 Scala 開…

【學習筆記】Burnside引理,Pólya定理及其應用

Burnside引理 書接上回,繼續深入研究在群作用下集合的軌道與穩定子群的相關性質 現在我們想要研究這樣一個問題: 有限群 G 在有限集合 S 上面有一個作用,那么 S 的 G ? 軌道條數是多少 有限群G在有限集合S上面有一個作用,那么…

【投稿優惠|檢索穩定】2024年信息系統、工程與數字化經濟國際會議(ICISEDE 2024)

2024年信息系統、工程與數字化經濟國際會議(ICISEDE 2024) 2024 International Conference on Information Systems and Engineering and the Digital Economy(ICISEDE 2024) [會議簡介] 2024 年信息系統、工程與數字化經濟國際會議(ICISEDE 2024)將于 2024 年 1 月 20 日在廈門…

Endnote在word中加入參考文獻及自定義參考文獻格式方式

第一部分:在word中增加引用步驟 1、先下載對應文獻的endnote引用格式,如在谷歌學術中的下載格式如下: 2、在endnote中打開存儲env的格式庫,導入對應下載的文件格式:file>import>file>choose,import對應文件&a…

IT外包駐場加速企業IT創新

隨著科技的快速發展,企業在追求創新和應用IT技術方面面臨挑戰。IT外包駐場服務成為許多企業的選擇,助力企業實現快速、高效的IT項目實施和應用。 IT外包駐場服務的主要目標是幫助企業在IT創新方面取得突破。這種服務模式不僅僅是提供技術支持&#xff0c…

3D建模基礎教程:模型UV講解

本篇文章將帶你探索3D建模中的模型UV。了解UV有助于你在3D建模中更好地進行紋理映射和材質應用,從而創建出更加逼真的3D場景。 uv坐標: UV坐標是用于映射紋理到3D模型表面的2D坐標。它們將2D紋理圖像映射到3D模型的3D空間上,使模型表面在渲…

配電室無人值守改造

配電室無人值守改造是通過運用先進的技術和設備,將傳統的需要人工值守的配電室改造成可以遠程監控和管理的智能化配電室,從而實現無人值守。這種改造可以提高配電室的安全性、可靠性和效率,降低運維成本。 建立智能監控系統:通過安…

Vue3選項式-基礎部分篇

Vue3選項式風格-基礎部分篇 簡介模板語法文本插值原始HTMLAttribute 綁定使用 JavaScript 表達式調用函數全局組件調用內置指令動態參數注意事項 data()data()深度響應 methods有狀態的methods(防抖) DOM更新時機計算屬性class和style綁定條件渲染列表渲染數組變換偵聽事件處理…

Linux 系統設置cpu頻率

source_code: https://github.com/emagii/cpufrequtils cpufreq-set - A small tool which allows to modify cpufreq settings.(修改內存頻率的工具) cpufreq-set allows you to modify cpufreq settings without having to type e.g. “/sys/devices…

echart中定義brush,默認狀態,觸發狀態

1.定義矩形選擇筆刷:brush 2.設置brush的默認狀態和選中邏輯

理解VAE(變分自編碼器)

1.貝葉斯公式 貝葉斯理論的思路是,在主觀判斷的基礎上,先估計一個值(先驗概率),然后根據觀察的新信息不斷修正(可能性函數)。 P(A):沒有數據B的支持下,A發生的概率,也叫做先驗概率。…

小視頻怎么做成二維碼?視頻二維碼3步生成

在日常工作和生活中經常會看到各種類型的小視頻、短視頻,比如網頁、抖音等等的視頻都是可以下載查看的。當我們想要將下載視頻分享給多個人看時,生成二維碼的方式會更加的方便,那么視頻如何生成二維碼呢?下面就將快捷生成二維碼的…

AI:90-基于深度學習的自然災害損害評估

?? 本文選自專欄:人工智能領域200例教程專欄 從基礎到實踐,深入學習。無論你是初學者還是經驗豐富的老手,對于本專欄案例和項目實踐都有參考學習意義。 ??? 每一個案例都附帶有在本地跑過的核心代碼,詳細講解供大家學習,希望可以幫到大家。歡迎訂閱支持,正在不斷更新…

第75講:MySQL數據庫MVCC多版本并發控制核心概念以及底層原理

文章目錄 1.當前讀與快照讀的基本概念1.1.當前讀的基本概念1.2.快照讀的基本概念 2.什么是MVCC多版本并發控制3.MVCC多版本并發控制依賴的三個組件重要概念3.1.MySQL表中三個隱式字段的概念3.2.undo log日志以及版本鏈的概念3.3.ReadView讀視圖的概念 4.MVCC實現多版本并發控制…

【FPGA】Verilog:BCD 加法器的實現

0x00 XOR 運算在 2 的補碼加減法中的應用 2 的補碼加減法的特點是,當從某個數中減去負數時,將其轉換為正數的加法來計算,并將減去正數的情況轉換為負數的加法來計算,從而將所有減法運算轉換為加法運算。在這種情況下,…