樹[數據結構]

一.樹的定義

1.什麼是樹

有且只有一個根節點,有若干個互不相交的子樹。
這裏寫圖片描述

2.專業術語

這裏寫圖片描述

如上圖:我們解釋父節點、子節點、兄弟節點、堂兄弟節點。

父節點:A爲父節點
子節點:B、C是A的子節點
兄弟節點:B和C是兄弟節點
堂兄弟節點:D與E是堂兄弟節點
深度:樹中節點的最大層次(從根節點到最底層節點的層數,圖中深度爲3)
葉子節點:沒有子節點的節點(B、E是葉子節點)
度:子節點的個數

二.樹的分類

1.一般樹

一般樹:任意一個節點的子節點個數都不受限制。

2.二叉樹

二叉樹:任意一個節點的子節點個數最多爲2個,且子節點位置不可更改。

3.森林

森林:n個互不相交的樹的集合。

擴展:二叉樹的分類

1.一般二叉樹
2.滿二叉樹:在不增加樹層次的前提下,不可添加新節點
3.完全二叉樹:將滿二叉樹最底層最右邊的連續若干個節點

如圖,是一個滿二叉樹:
這裏寫圖片描述

變成完全二叉樹,可以不刪除節點(完全二叉樹包含滿二叉樹),也可以刪除 G、GE、GEF、GEFD.

三.樹的存儲

1.連續存儲:

連續存儲必須是完全二叉樹的形式
爲什麼呢?


如下圖,一顆普通樹:
這裏寫圖片描述

假設我們使用層次的關係(從左到右,從上到下的關係來存儲)

該樹在數組中的存儲結構如下:
這裏寫圖片描述


我們根據此數組將樹還原:

到底是這種形式呢?
這裏寫圖片描述

還是這種形式呢?

這裏寫圖片描述

有歧義,不能確定節點的邏輯關係。
也就是,如果一棵樹不是完全二叉樹的形式,我們不能根據連續的存儲結構來還原一棵樹。
所以,連續的存儲結構必須是完全二叉樹的形式。

我們可以如下存儲:

這裏寫圖片描述

至此,我們總結一下連續結構存儲完全二叉樹的優缺點:

優點:
1.查找某個節點的父節點與子節點很快
2.可以通過編號確定節點在第幾層

缺點:
浪費空間(如上面存儲,還得存儲空節點)

2.鏈式存儲

我們將樹的節點分爲3部分構成:
pLeft指向左子樹、pRight指向右子樹、data存放節點數據。
如下圖:
這裏寫圖片描述

我們可以使用 先序遍歷、中序遍歷、後序遍歷 遍歷這棵樹。

鏈式存儲相對於連續存儲來說,更節省空間,所以使用範圍廣。

3.非鏈式存儲

對於下面樹的存儲:
這裏寫圖片描述

1.雙親表示法

這裏寫圖片描述
A:-1(代表A是根節點)
B:0(代表B的父節點是A)
C:0(代表C的父節點是A)
D:1(代表D的父節點是B)
E:1(代表E的父節點是B)

特點:求父節點很方便,但是求子節點麻煩。

2.孩子表示法

這裏寫圖片描述
特點:求子節點很方便,但是求父節點麻煩。

3.雙親-孩子表示法

這裏寫圖片描述
特點:求子節點很方便,求父節點也很方便,但是浪費空間。

4.二叉樹表示法

對於一般樹的存儲,我們可以使用雙親表示法、孩子表示法、雙親-孩子表示法。
我們也可以將一棵樹轉換成二叉樹來存儲。
因爲現在,我們對二叉樹的算法比較成熟,對普通樹的算法不成熟。所以我們通常將普通樹轉換成二叉樹來存儲,便於以後的操作。

我們將一顆普通樹轉變爲二叉樹來存儲的規則是:左指針域指向第1個孩子,右指針域指向兄弟。

這裏寫圖片描述

總結:
連續存儲結構:浪費空間(必須是完全二叉樹),但是查找速度快
鏈式存儲結構:節約空間,查找速度還可以
非鏈式存儲結構:最浪費空間
我們最常用的存儲結構是:鏈式二叉樹

四.樹的操作

1.遍歷

這裏寫圖片描述

1.1前序遍歷

前序遍歷的順序是:根節點-左子樹-右子樹
上面圖片樹的前序遍歷爲:A-B-D-E-C-F

1.2中序遍歷

中序遍歷的順序是:左子樹-根節點-右子樹
上面圖片樹的前序遍歷爲:D-B-E-A-F-C

1.3後序遍歷

後序遍歷的順序是:左子樹-右子樹-根節點
上面圖片樹的前序遍歷爲:D-E-B-F-C-A

2.根據遍歷結果還原一棵樹

我們可以根據:
先序遍歷+中序遍歷—>還原一棵樹
中序遍歷+後序遍歷—>還原一棵樹
但是不能:
前序遍歷+後序遍歷—>還原一棵樹

因爲:
前序遍歷可以確定根節點,但是不能確定左子樹、右子樹
後序遍歷可以確定根節點,但是不能確定左子樹、右子樹
中序遍歷可以確定左子樹、右子樹,但是不能確定根節點
所以:
先序遍歷或後序遍歷只有和中序遍歷結合,才能確定一棵樹的根節點、左子樹、右子樹。

我們在根據 先序+中序—>還原一棵樹 或 中序+後序—>還原一棵樹 的時候,有如下規律:

先序:最先出現的是根節點
後序:最後出現的是根節點
我們根據先序和後序的規律,再結合中序,就能輕鬆的還原一棵樹。

具體的練習就不寫了,其實並不難。

五.樹的應用

1.是數據庫中數據組織的一種重要方式(如圖書管理數據庫)
2.操作系統子父進程的關係就是一棵樹。
3.面向對象中類的繼承關係
等等…

六.編碼實戰

我們創建下面圖中的這棵樹,並且實現它的前序遍歷,中序遍歷、後序遍歷。

這裏寫圖片描述

#include <stdio.h>
#include <stdlib.h>
struct BTNode
{
    char data;
    struct BTNode * PTLchild;
    struct BTNode * PTRchild;
};
struct BTNode *createBTree();
void preTraverseBTree();
void inTraverseBTree();
void postTraverseBTree();
int main()
{
    struct BTNode * pT=createBTree();
    preTraverseBTree(pT);
    inTraverseBTree(pT);
    postTraverseBTree(pT);
    return 0;
}
//創建一棵樹
struct BTNode *createBTree()
{
    struct BTNode * pA=(struct BTNode *)malloc(sizeof(struct BTNode));
    struct BTNode * pB=(struct BTNode *)malloc(sizeof(struct BTNode));
    struct BTNode * pC=(struct BTNode *)malloc(sizeof(struct BTNode));
    struct BTNode * pD=(struct BTNode *)malloc(sizeof(struct BTNode));
    struct BTNode * pE=(struct BTNode *)malloc(sizeof(struct BTNode));
    pA->data='A';
    pB->data='B';
    pC->data='C';
    pD->data='D';
    pE->data='E';
    pA->PTLchild=pB;
    pA->PTRchild=pC;
    pB->PTLchild=NULL;
    pB->PTRchild=NULL;
    pC->PTLchild=pD;
    pC->PTRchild=NULL;
    pD->PTLchild=NULL;
    pD->PTRchild=pE;
    pE->PTLchild=NULL;
    pE->PTRchild=NULL;
    return pA;
}
//先序遍歷
void preTraverseBTree(struct BTNode * pT)
{
    if(pT!=NULL)
    {
        printf("%c\n",pT->data);
        if(pT->PTLchild!=NULL)
        {
             preTraverseBTree(pT->PTLchild);
        }
        if(pT->PTRchild!=NULL)
        {
            preTraverseBTree(pT->PTRchild);
        }
    }
}
//中序遍歷
void inTraverseBTree(struct BTNode * pT)
{
    if(pT!=NULL)
    {
        if(pT->PTLchild!=NULL)
        {
             inTraverseBTree(pT->PTLchild);
        }
        printf("%c\n",pT->data);
        if(pT->PTRchild!=NULL)
        {
            inTraverseBTree(pT->PTRchild);
        }
    }

}
//後序遍歷
void postTraverseBTree(struct BTNode * pT)
{
    if(pT!=NULL)
    {
        if(pT->PTLchild!=NULL)
        {
             postTraverseBTree(pT->PTLchild);
        }
        if(pT->PTRchild!=NULL)
        {
            postTraverseBTree(pT->PTRchild);
        }
        printf("%c\n",pT->data);
    }

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