工科生一枚,熱衷於底層技術開發,有強烈的好奇心,感興趣內容包括單片機,嵌入式Linux,Uboot等,歡迎學習交流!
愛好跑步,打籃球,睡覺。 歡迎加我QQ1500836631(備註CSDN),一起學習交流問題,分享各種學習資料,電子書籍,學習視頻等。
樹的基本概念
圖1
樹的結點
結點:使用樹結構存儲的每一個數據元素都被稱爲“結點”。例如,上圖1中,數據元素 1 就是一個結點;
父結點(雙親結點)、子結點和兄弟結點:對於上圖1中的結點 1,2,3,4 來說,1 是 2,3,4 結點的父結點(也稱爲“雙親結點”),而 2,3,4 都是 1 結點的子結點(也稱“孩子結點”)。對於 2,3,4 來說,它們都有相同的父結點,所以它們互爲兄弟結點。
樹根結點(簡稱“根結點”):每一個非空樹都有且只有一個被稱爲根的結點。上圖1中,結點1就是整棵樹的根結點。
葉子結點:如果結點沒有任何子結點,那麼此結點稱爲葉子結點(葉結點)。例如上圖1中,結點 11,12,6,7,13,9,10都是這棵樹的葉子結點。
子樹和空樹
子樹:上圖1中,整棵樹的根結點爲結點 1,而如果單看結點2,5,6,11,12 組成的部分來說,也是棵樹,而且節點 2 爲這棵樹的根結點。所以稱 2,5,6,11,12這幾個結點組成的樹爲整棵樹的子樹;同樣,結點 5,11,12 構成的也是一棵子樹,根結點爲5。
注意:單個結點也是一棵樹,只不過根結點就是它本身。上圖1中,結點 11,12,6 等都是樹,且都是整棵樹的子樹。
知道了子樹的概念後,樹也可以這樣定義:樹是由根結點和若干棵子樹構成的。
空樹:如果集合本身爲空,那麼構成的樹就被稱爲空樹。空樹中沒有結點。
補充:在樹結構中,對於具有同一個根結點的各個子樹,相互之間不能有交集。例如,上圖1中,除了根結點1,其餘元素又各自構成了三個子樹,根結點分別爲 2,3,4,這三個子樹相互之間沒有相同的結點。如果有,就破壞了樹的結構,不能算做是一棵樹。
結點的度和層次
對於一個結點,擁有的子樹數(結點有多少分支)稱爲結點的度(Degree)。例如,上圖1中,根結點1下分出了 3 個子樹,所以,結點 1 的度爲 3。
一棵樹的度是樹內各結點的度的最大值。上圖1表示的樹中,各個結點的度的最大值爲 3,所以,整棵樹的度的值是 3。
結點的層次:從一棵樹的樹根開始,樹根所在層爲第一層,根的孩子結點所在的層爲第二層,依次類推。對於上圖1來說,1 結點在第一層,2,3,4 爲第二層,5,6,7,8,9,10在第三層,11,12,13在第四層。
一棵樹的深度(高度) 是樹中結點所在的最大的層次。上圖1樹的深度爲 4。
如果兩個結點的父結點雖不相同,但是它們的父結點處在同一層次上,那麼這兩個結點互爲堂兄弟。例如,上圖1中,結點5,6,7,8,9,10 的父結點都在第二層,所以之間爲堂兄弟的關係。
有序樹和無序樹
如果樹中結點的子樹從左到右看,誰在左邊,誰在右邊,是有規定的,這棵樹稱爲有序樹;反之稱爲無序樹。
在有序樹中,一個結點最左邊的子樹稱爲"第一個孩子",最右邊的稱爲"最後一個孩子"。
拿上圖1來說,如果是其本身是一棵有序樹,則以結點 2 爲根結點的子樹爲整棵樹的第一個孩子,以結點 4 爲根結點的子樹爲整棵樹的最後一個孩子。
森林
由 m(m >= 0)個互不相交的樹組成的集合被稱爲森林。上圖1中,分別以2,3,4爲根結點的三棵子樹就可以稱爲森林。
前面講到,樹可以理解爲是由根結點和若干子樹構成的,而這若干子樹本身是一個森林,所以,樹還可以理解爲是由根結點和森林組成的。用一個式子表示爲:Tree =(root,F)
其中,root 表示樹的根結點,F 表示由 m(m >= 0)棵樹組成的森林。
二叉樹的性質
經過前人的總結,二叉樹具有以下幾個性質:
- 二叉樹中,第 i 層最多有 2i-1 個結點。
- 如果二叉樹的深度爲 K,那麼此二叉樹最多有 2K-1 個結點。
- 二叉樹中,終端結點數(葉子結點數)爲 n0,度爲 2 的結點數爲 n2,則 n0=n2+1。
- 二叉樹還可以繼續分類,衍生出滿二叉樹和完全二叉樹。
滿二叉樹
如果二叉樹中除了葉子結點,每個結點的度都爲 2,則此二叉樹稱爲滿二叉樹。
圖2
如圖2 所示就是一棵滿二叉樹。
- 滿二叉樹除了滿足普通二叉樹的性質,還具有以下性質:
- 滿二叉樹中第 i 層的節點數爲 2n-1 個。
- 深度爲 k 的滿二叉樹必有 2k-1 個節點 ,葉子數爲 2k-1。
- 滿二叉樹中不存在度爲 1 的節點,每一個分支點中都兩棵深度相同的子樹,且葉子節點都在最底層。
- 具有 n 個節點的滿二叉樹的深度爲 log2(n+1)。
完全二叉樹
如果二叉樹中除去最後一層節點爲滿二叉樹,且最後一層的結點依次從左到右分佈,則此二叉樹被稱爲完全二叉樹。
圖3
如上圖3a 所示是一棵完全二叉樹,如上圖3b中由於最後一層的節點沒有按照從左向右分佈,因此只能算作是普通的二叉樹。
完全二叉樹除了具有普通二叉樹的性質,它自身也具有一些獨特的性質,比如說,n 個結點的完全二叉樹的深度爲 ⌊log2n⌋+1。
[log2n]表示取小於 log2n 的最大整數。例如,[log24] = 2,而 [log25⌋]結果也是 2。
對於任意一個完全二叉樹來說,如果將含有的結點按照層次從左到右依次標號(圖3),對於任意一個結點 i ,完全二叉樹還有以下幾個結論成立:
- 當 i>1 時,父親結點爲結點 [i/2] 。(i=1 時,表示的是根結點,無父親結點)
- 如果 2 * i>n(總結點的個數) ,則結點 i 肯定沒有左孩子(爲葉子結點);否則其左孩子是結點 2*i 。
- 如果 2 * i+1>n ,則結點 i 肯定沒有右孩子;否則右孩子是結點 2*i+1 。
二叉樹的順序存儲
二叉樹的順序存儲,指的是使用順序表(數組)存儲二叉樹。需要注意的是,順序存儲只適用於完全二叉樹。換句話說,只有完全二叉樹纔可以使用順序表存儲。因此,如果我們想順序存儲普通二叉樹,需要提前將普通二叉樹轉化爲完全二叉樹。
有讀者會說,滿二叉樹也可以使用順序存儲。要知道,滿二叉樹也是完全二叉樹,因爲它滿足完全二叉樹的所有特徵。
普通二叉樹轉完全二叉樹的方法很簡單,只需給二叉樹額外添加一些節點,將其"拼湊"成完全二叉樹即可。如圖4所示:
圖4
下圖中,左側是普通二叉樹,右側是轉化後的完全(滿)二叉樹。
解決了二叉樹的轉化問題,接下來學習如何順序存儲完全(滿)二叉樹。
完全二叉樹的順序存儲,僅需從根節點開始,按照層次依次將樹中節點存儲到數組即可。
圖5
例如,存儲圖5如下 所示的完全二叉樹,其存儲狀態如圖 6 所示:
圖6
同樣,存儲由普通二叉樹轉化來的完全二叉樹也是如此。例如,圖 4 中普通二叉樹的數組存儲狀態如圖7 所示:
圖7
由此,我們就實現了完全二叉樹的順序存儲。
不僅如此,從順序表中還原完全二叉樹也很簡單。我們知道,完全二叉樹具有這樣的性質,將樹中節點按照層次並從左到右依次標號(1,2,3,…),若節點 i 有左右孩子,則其左孩子節點爲 2i,右孩子節點爲 2i+1。此性質可用於還原數組中存儲的完全二叉樹,也就是實現由圖6到圖5、由圖 7到圖4的轉變。
二叉樹的鏈式存儲
二叉樹並不適合用數組存儲,因爲並不是每個二叉樹都是完全二叉樹,普通二叉樹使用順序表存儲或多或多會存在空間浪費的現象。
接下來我們介紹二叉樹的鏈式存儲結構。
圖8
如圖 8 所示,此爲一棵普通的二叉樹,若將其採用鏈式存儲,則只需從樹的根節點開始,將各個節點及其左右孩子使用鏈表存儲即可。因此,圖8對應的鏈式存儲結構如圖9所示:
圖9
由圖9可知,採用鏈式存儲二叉樹時,其節點結構由 3 部分構成(如圖10 所示):
- 指向左孩子節點的指針(Lchild);
- 節點存儲的數據(data);
- 指向右孩子節點的指針(Rchild);
圖10
鏈式存儲代碼實現:
/*
* @Description: 二叉樹的鏈式存儲
* @Version: V1.0
* @Autor: Carlos
* @Date: 2020-05-29 16:37:38
* @LastEditors: Carlos
* @LastEditTime: 2020-05-29 16:45:13
*/
#include <stdio.h>
#include <stdlib.h>
typedef struct BitNode
{
int data;
struct BitNode *lchild,*rchild;
}BitNode,*BitTree;
void CreateBiTree(BitTree *T){
*T=(BitNode*)malloc(sizeof(BitNode));
//根節點
(*T)->data=1;
(*T)->lchild=(BitNode*)malloc(sizeof(BitNode));
//1節點的左孩子2
(*T)->lchild->data=2;
(*T)->rchild=(BitNode*)malloc(sizeof(BitNode));
//1節點的右孩子3
(*T)->rchild->data=3;
(*T)->rchild->lchild=NULL;
(*T)->rchild->rchild=NULL;
(*T)->lchild->lchild=(BitNode*)malloc(sizeof(BitNode));
//2節點的左孩子
(*T)->lchild->lchild->data=4;
(*T)->lchild->rchild=NULL;
(*T)->lchild->lchild->lchild=NULL;
(*T)->lchild->lchild->rchild=NULL;
}
int main() {
BitTree Tree;
CreateBiTree(&Tree);
printf("%d",Tree->lchild->lchild->data);
return 0;
}
文中代碼均已測試,有任何意見或者建議均可聯繫我。歡迎學習交流!
如果覺得寫的不錯,請點個贊再走,謝謝!