模塊二 非線性結構
樹
定義
- 有且只有一個稱爲根的節點
- 有若干個互不相交的子樹,這些子樹本身也是一棵樹
通俗定義
- 樹是由節點和邊組成
- 但有一個節點例外,該節點沒有父節點,此節點稱爲根節點
- 每個節點只有一個父節點但可以有多個子節點
專業術語
- 節點
- 父節點
- 子節點
- 子孫
- 兄弟
- 堂兄弟
- 深度:從根節點到最底層節點的層數稱爲深度,根節點是第一層
- 葉子節點:沒有子節點的節點
- 非終端節點:實際就是非葉子節點
- 度:子節點的個數稱爲度(D的度3 樹的度指最大的度 下圖爲3)
分類
- 一般樹:任意一個節點的子節點的個數都不受限制
- 二叉樹: 任意一個節點的子節點的個數最多兩個,且子節點的位置不可更改(有序)
- 森林:n個互不相交的樹的集合
二叉樹的分類
- 一般二叉樹
- 滿二叉樹 :在一棵二叉樹中,如果所有分支結點都存在左子樹和右子樹,並且所有葉子都在同一層上,這樣的二叉樹稱爲滿二叉樹。
- 完全二叉樹: 如果只是刪除了滿二叉樹最底層最右邊的連續若干個節點,這樣形成的二叉樹就是完全二叉樹
樹的存儲
二叉樹存儲
- 連續存儲【完全二叉樹】
優點:查找某個節點的父節點和子節(也包括判斷有沒有子節點)速度很快
缺點:耗內存空間很大
- 鏈式存儲
一般樹存儲
- 雙親表示法 求父節點方便
- 孩子表示法 求子節點方便
- 雙親孩子表示法 求父節點和子節點都方便
- 二叉樹表示法 孩子兄弟表示法 把一個普通樹轉化成二叉樹來存儲
具體轉換方法:設法保證任意一個節點的左指針域指向他的第一個孩子,右指針域指向他的兄弟,只要能滿足此條件,就可以把一個普通書轉化爲二叉樹,一個普通樹轉化成的二叉樹一定沒有右子樹
森林的存儲
先把森林轉化爲二叉樹,然後再存儲二叉樹(下圖可省略步驟1)
操作
- 遍歷
先序遍歷
先訪問根節點
再先序訪問左子樹
再線序訪問右子樹
中序遍歷
中序遍歷左子樹
再訪問根節點
再中序遍歷右子樹
後序遍歷
後序遍歷左子樹
後序遍歷右子樹
再訪問根節點
左到右先葉子後結點的方式遍歷訪問左右子樹,最後是訪問根結點
- 已知兩種遍歷順序求原始二叉樹
通過先序和中序 或者 中序和後序可以還原出原始的二叉樹,但是通過先序和後序是無法還原出二叉樹的
實例一
實例二
實例三
樹的應用
- 樹是數據庫中數據組織一種重要形式
- 操作系統子父進程的關係本身就是一顆樹
- 面嚮對象語言中類的繼承關係
- 赫夫曼樹
程序演示
# include <stdio.h>
# include <malloc.h>
struct BTNode
{
char data;
struct BTNode *pLchild; // p是指針 L是左 child是孩子
struct BTNode *pRchild;
};
struct BTNode * CreateBTree();
void PreTraverseBTree(struct BTNode *pT);
void InTraverseBTree(struct BTNode *pT);
void PostTraverseBTree(struct BTNode *pT);
int main(void)
{
struct BTNode *pT = CreateBTree();
//PreTraverseBTree(pT);
//InTraverseBTree(pT);
PostTraverseBTree(pT);
return 0;
}
struct BTNode * CreateBTree(void)
{
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->pLchild = pB;
pA->pRchild = pC;
pB->pLchild = pB->pRchild = NULL;
pC->pLchild = pD;
pC->pRchild = NULL;
pD->pLchild = NULL;
pD->pRchild = pE;
pE->pLchild = pE->pRchild = NULL;
return pA;
}
void PreTraverseBTree(struct BTNode *pT)
{
/*先訪問根節點
再先序訪問左子樹
再先序訪問右子樹*/
if (NULL != pT)
{
printf("%c ", pT->data);
if (NULL != pT->pLchild)
{
PreTraverseBTree(pT->pLchild);
}
if (NULL != pT->pRchild)
{
PreTraverseBTree(pT->pRchild);
}
}
}
void InTraverseBTree(struct BTNode *pT)
{
if (NULL != pT)
{
if (NULL != pT->pLchild)
{
InTraverseBTree(pT->pLchild);
}
printf("%c ", pT->data);
if (NULL != pT->pRchild)
{
InTraverseBTree(pT->pRchild);
}
}
}
void PostTraverseBTree(struct BTNode *pT)
{
if (NULL != pT)
{
if (NULL != pT->pLchild)
{
PostTraverseBTree(pT->pLchild);
}
if (NULL != pT->pRchild)
{
PostTraverseBTree(pT->pRchild);
}
printf("%c ", pT->data);
}
}