基本數據結構 -- 樹簡介

1、什麼是樹算法

  樹是一個有限結點組成的集合。能夠用遞歸的方式來定義一棵樹:樹能夠是一個空集,若非空,則一棵樹由一個根(root)結點 r 以及 0 個或多個非空的(子)樹 T一、T二、T三、...,Tk 組成,這些子樹中的每一棵的根都被來自根 r 的一條有向的邊(edge)所鏈接。每一棵子樹的根叫作根 r 的兒子(child),而 r 是每一棵子樹的根的父親(parent)數據結構

樹的幾個概念:spa

1)樹葉(leaf):從上述的遞歸定義能夠知道,一棵樹是由 N 個結點和 N-1 條邊組成的集合。每一個結點均可以有零個或任意多個兒子,沒有兒子的結點稱爲樹葉(leaf);指針

2)兄弟(sibling):具備相同父親的結點稱爲兄弟(sibling);code

3)路徑(path):從結點 n1 到 nk 路徑定義爲結點 n1、n2、n3、...、nk 的一個序列,使得對於 1 <= i  <= k,結點 ni 是 ni+1 的父親。這個路徑的長爲該路徑上的邊的條數,即 k-1。從每個結點到它本身有一條長爲 0 的路徑。在一棵樹中,從根到每一個結點剛好存在一條路徑blog

4)深度(depth):對於任意結點 ni,ni 的深度爲從根到 ni 的惟一路徑的長,根的深度爲0;排序

5)高(height):對於任意結點 ni,ni 的高爲從 ni 到一片樹葉的最長路徑的長,樹的高等於它的根的高(只有一個根結點的樹的高度爲0,空樹的高度爲 -1);遞歸

6)結點的度:一個結點的兒子的個數。內存

圖 1-1 樹io

  如圖所示,爲一棵樹,其中,A是根結點;B、C、D、E、F 爲 A 的兒子,A 爲它們的父親;B 和 C有相同的父親,它們是兄弟(sibling);從結點 A 到結點 M 的路徑爲 A、D、H、M;結點 B 的深度爲1;結點 D 的高度爲 3;整棵樹的高度爲 4。

 

2、二叉樹

  二叉樹是一棵樹,其中每一個結點最多有兩個兒子,分別爲左子結點和右子結點。二叉樹的平均深度是 O(√N)。

2.1 二叉樹的實現

  由於一棵二叉樹的每一個結點最多隻能有兩個子結點,故而咱們能夠用指針直接指向它們。樹結點的聲明在結構上相似於雙鏈表的聲明,在聲明中,一個結點由關鍵字(Key)和兩個指向其餘結點的指針(Left 和 Right)組成:

typedef int ElementType;

struct TreeNode {
    ElementType Element;
    struct TreeNode *Left;
    struct TreeNode *Right;
};

typedef struct TreeNode *PtrToNode;
typedef PtrToNode BinaryTree;

  這段代碼聲明瞭一個結構,該結構包含一個 ElementType 類型的數據,用於保存結點數據;一個 Left 指針,指向結點的左子樹;和一個 Right 指針,指向結點的右子樹。

 

2.2 二叉樹的遍歷

  按必定的順序,依次訪問二叉樹中的全部結點的操做稱爲遍歷。有三種方式能夠對二叉樹進行遍歷,如下面這幅圖爲例進行介紹:

圖 2-1 二叉樹

1)先序遍歷

  先訪問根結點,而後遍歷左子樹,再遍歷右子樹。對圖 2-1 所示的二叉樹進行先序遍歷結果爲:A-B-D-G-H-E-C-F-I-J。 先序遍歷一棵二叉樹的C語言實現以下:

/* 先序遍歷,遞歸實現 */
void PreOrderRecursion(BinaryTree T)
{
    if (T != NULL) {
        printf("%d\t", T->Element);
        PreOrderRecursion(T->Left);
        PreOrderRecursion(T->Right);
    }
}

  上述代碼用遞歸的方式實現了先序遍歷二叉樹。須要注意的是判斷條件,要時刻判斷二叉樹是否爲空,這在遞歸中尤其重要。遞歸實現先序遍歷的代碼十分簡潔且易懂。可是,遞歸調用的空間複雜度較大,當輸入規模很大時,會佔用至關多的內存,也容易形成堆棧的溢出。

  可使用非遞歸的方式來實現先序遍歷,大體思想以下:首先,將根結點保存到一個數據結構裏,而後訪問左子樹,待左子樹訪問完後,取出根結點,再訪問右子樹;每訪問一個子樹以前,都將這棵子樹的根結點保存,待須要訪問其右子樹時再取出。能夠知道的是,先保存進去的結點數據後取出,是一個後入先出結構,所以使用棧來保存根結點的數據十分合適。

 

2)後序遍歷

  先遍歷左子樹,再遍歷右子樹,最後訪問根結點。對圖 2-1 所示的二叉樹進行後序遍歷的結果爲:G-H-D-E-B-I-J-F-C-A。

/* 後序遍歷,遞歸實現 */
void PostOrderRecursion(BinaryTree T)
{
    if (T != NULL) {
        PostOrderRecursion(T->Left);
        PostOrderRecursion(T->Right);
        printf("%d\n", T->Element);
    }
}

 

3)中序遍歷

  先遍歷左子樹,而後訪問根結點,再遍歷右子樹。對圖 2-1 所示的二叉樹進行中序遍歷的結果爲:G-D-G-B-E-A-C-I-F-J。

/* 中序遍歷,遞歸實現 */
void InOrderRecursion(BinaryTree T)
{
    if(T != NULL){
        InOrderRecursion(T->Left);
        printf("%d\n",T->Element);
        InOrderRecursion(T->Right);
    }
}

 

3、一些特殊的二叉樹

3.1 滿二叉樹

  滿二叉樹是一棵深度爲 k,且有 2^(k-1) 個結點的二叉樹,以下圖所示:

圖 2-2 滿二叉樹

  這就是一個滿二叉樹,能夠看到,滿二叉樹的全部葉子都在同一層  ——  最後一層,非葉子結點的度必定爲2。在一樣深度的二叉樹中,滿二叉樹的結點數是最多的,葉子數也是最多的。

 

3.2 徹底二叉樹

  徹底二叉樹,除了最後一層外,其他層都是滿的,且最後一層的葉子結點都幾種在樹的左邊。滿二叉樹就是徹底二叉樹的一個特例。以下圖:

圖 2-3 徹底二叉樹

 

3.3 二叉查找樹(二叉排序樹)

  二叉查找樹是二叉樹的一種,更適合於進行查找操做。對於二叉查找樹,樹中的每一個結點 X 的左子樹中全部關鍵字的值小於 X 的關鍵字值,而它的右子樹中全部關鍵字值大於 X 的關鍵字值。這意味着,該樹的全部元素能夠以某種統一的方式進行排序。二叉查找樹的平均深度是 O(logN)。 

 

圖 2-4 二叉查找樹

 

參考資料:

《算法導論 第三版》

《數據結構與算法分析--C語言描述》

相關文章
相關標籤/搜索