數據結構 —— 樹 (相關概念)

樹,都知道吧!不過在程序員的數據結構裏,就是把樹倒過來(當然是樹形數據結構的抽象描述了)!

樹的定義:

樹(Tree),是 n(n >= 0)個結點的有限集合。當 n = 0時,該集合滿足以下條件:
(1)每個元素稱爲結點(node);
(2)有一個特定的結點被稱爲根結點或樹根(root)。
(3)除根結點之外的其餘數據元素被分爲m(m≥0)個互不相交的集合T1,T2,……Tm-1,其中每一個集合Ti(1<=i<=m)本身也是一棵樹,被稱作原樹的子樹(subtree)。

樹的基本術語:

結點(Node): 包含一個數據元素以及若干指向其子樹的分支。(C: 數據域 + 指針域)
結點的度: 擁有子樹的個數稱爲該結點的度。
樹的度: 樹中所有結點的度的最大值。
葉子結點:度爲0 的結點稱爲葉子結點,也成爲終端結點。
結點的層次:結點的層次從樹根開始定義,根爲第一層,根的孩子爲第二層。。。。
樹的深度:樹中所有結點層次的最大值稱爲樹的深度。

二叉樹

定義:每個結點的度不大於2的特殊樹。子樹分左、右子樹。

二叉樹的性質 (重要)

1、在二叉樹的第i層上至多有 2^(i-1) 個結點。(i >= 1)
2、深度爲 k 的二叉樹至多有 2^i -1 個結點。
3、對於任意一顆二叉樹T,若葉子結點數爲 n0,度爲 2 的結點數爲 n2 ,則 n0 = n2 + 1 ;

提出概念:

滿二叉樹:深度爲 k 且含有 2^k -1 個結點的二叉樹稱爲滿二叉樹。
解釋:就是除了最後一層的葉子結點,其餘結點都有2個子樹。
完全二叉樹:我稱爲差一點成了滿二叉樹的二叉樹,即一層一層排,一層滿了下一層,就是最後一層可能不完美,小於等於  2^(i-1) 個結點。完美了就是滿二叉樹。所以滿二叉樹就是完美的完全二叉樹。

4、具有 n 個結點的完全二叉樹的深度爲 log2N +1。
5、對於具有 n 個結點的完全二叉樹,如果按照滿二叉樹結點進行連續編號的方式,對所有結點從 1 開始順序編號,則對於任意序號爲 i 的結點有:
①、如果 i=1,則結點 i 爲根;如果 i >1 ,則結點i 的雙親結點序號爲 i/1;
②、如果 2i <= n,則結點i 的左孩子結點序號爲 2i ,否則,結點 i 無左孩子。
③、如果 2i+1 <= n,則結點i 的左孩子結點序號爲 2i+1,否則,結點 i 無右孩子。

二叉樹的存儲:

常用的有兩種,順序存儲、鏈式存儲。

順序存儲:

建立數組,按照滿二叉樹給結點編號,然後把結點放在數組中編號對應的位置上。

存在問題:對於一般的二叉樹,比較浪費空間,合適滿二叉樹或者完全二叉樹。

鏈式存儲:

結點結構分爲三個域:數據域,左孩子域,右孩子域。
左孩子域指向該結點的左孩子,右孩子域指向該結點的右孩子。
typedef struct Node
{
    DateType data;
    struct Node *LChild;
    struct Node *RChild;
}BiTNode, *BiTree;

//根據需要還可以建立指向父節點的域

二叉樹的遍歷:

有前序遍歷,中序遍歷,後序遍歷 和 層次遍歷。

前序遍歷、中序遍歷 和 後序遍歷 的實現:
//遞歸實現:前序遍歷(中序遍歷和後續遍歷類似)
void PreOrder(BiTree root)
{
    if(root)
    {
        Visit(root);//訪問根結點
        PreOrder(root->LChild);//前序遍歷左子樹
        PreOrder(root->RChild);//前序遍歷右子樹
    }
}

//非遞歸實現前序遍歷:(使用棧)
void PreOrder(BiTree root)
{
    stack<BiTree> s;//C++中的 棧 容器
    BiTree p;
    p = root;
    while(p != null | s.empty() != 0)
    {
        while(p != null)
        {
            Visit(p->data);//訪問數據域
            s.push(p);
            p = p->LChild;
        }
        if(s.empty() != 0)
        {
            p = s.top();//取棧頂
            s.pop();//退棧
            p = p->RChild;
        }
    } 
}

二叉樹進階

線索二叉樹:

前序線索二叉樹、中序線索二叉樹、後序線索二叉樹。

    概念:對於n個結點的二叉樹,在二叉鏈存儲結構中有n+1個空鏈域,利用這些空鏈域存放在某種遍歷次序下該結點的前驅結點和後繼結點的指針,這些指針稱爲線索,加上線索的二叉樹稱爲線索二叉樹。

    二叉樹的遍歷本質上是將一個複雜的非線性結構轉換爲線性結構,使每個結點都有了唯一前驅和後繼(第一個結點無前驅,最後一個結點無後繼)。對於二叉樹的一個結點,查找其左右子女是方便的,其前驅後繼只有在遍歷中得到。爲了容易找到前驅和後繼,有兩種方法。一是在結點結構中增加向前和向後的指針fwd和bkd,這種方法增加了存儲開銷,不可取;二是利用二叉樹的空鏈指針。

現將二叉樹的結點結構重新定義如下:

typedef struct Node
{
    DateType data;
    struct Node *LChild;
    int ltag;
    struct Node *RChild;
    int rtag;
}BiTNode, *BiTree;

其中:ltag=0 時lchild指向左子女;
ltag=1 時lchild指向前驅;
rtag=0 時rchild指向右子女;
rtag=1 時rchild指向後繼;

最優二叉樹:(哈夫曼樹)

用於:構造最優編碼,用於信息傳輸、數據壓縮等
相關概念:

路徑:從樹的一個結點到另一個結點的分支序列構成兩個結點間的路徑。
路徑長度:路徑上分支的條數稱爲路徑長度。
樹的路徑長度:從樹根到每個結點的路徑長度之和稱爲樹的路徑長度。
結點的權:給樹中的結點賦予一個數值,該數值稱爲結點的權。
帶權路徑長度:結點到樹根間的路徑長度與結點的權的成績,稱爲該結點的帶權路徑長度。
樹的帶權路徑長度WPL:樹中所有葉子結點的帶權路徑長度之和,稱爲樹的帶權路徑長度。
最優二叉樹:樹的帶權路徑長度WPL最小的二叉樹稱爲最優二叉樹。

二叉搜索樹(又:二叉查找樹。二叉排序樹):

它或者是一顆空樹,或者是具有以下特性的二叉樹:
①若它的左子樹不空,則左子樹上的所有結點的值均小於它的根結點的值;
②若它的右子樹不空,則右子樹上的所有結點的值均大於它的根結點的值;
③它的左、右子樹也分別是二叉搜索樹。

根據二叉搜索樹的特性,就可以排序時構造二叉搜索樹,查找時時間複雜度是O(log2N),最差的時候化爲線性查找時間複雜度爲O(N)。

爲了在每次查找時間複雜度都是 O()log2N。引出了平衡二叉樹。

B樹和B+樹

B樹:
不是二叉樹,而是樹,一種多路平衡二叉樹。
多路:指數的分支多於二叉;
平衡:值所有的葉子結點均在同一層。

B樹的階:樹中所有的結點的孩子結點的最大值稱爲B樹的階,通常用m來表示。(從查找效率來考慮,通常取 m>= 3)。

一個m階的B樹的定義:
該書或者是空樹,或者滿足以下性質的m叉樹。
①樹只每個結點至多有m個子結點;
②除根結點和葉子結點意外,其他每個結點至少有 m/2 個子結點;
③若根結點不是葉子結點,則根結點至少有兩顆子樹。
④所有的葉子結點在同一層,可以有 m/2 -1 到 m-1 個關鍵字,並且葉子結點所在的層數爲樹的深度。
⑤有 k 個子結點的分支結點恰好包含 k-1 個關鍵字。

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