二叉樹的基本概念介紹與代碼實現(多圖+代碼)

工科生一枚,熱衷於底層技術開發,有強烈的好奇心,感興趣內容包括單片機,嵌入式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;
}

  文中代碼均已測試,有任何意見或者建議均可聯繫我。歡迎學習交流!
  如果覺得寫的不錯,請點個贊再走,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章