樹定義:
- 有且只有一個稱為根的節點
- 有若干個互不相交的子樹,這些子樹本身也是一個樹
通俗的講:
- 樹是有結點和邊組成,
- 每個結點只有一個父結點,但可以有多個子節點
- 但有一個節點例外,該節點沒有父結點,稱為根節點
一、專業術語
結點、父結點、子結點、根結點
深度:從根節點到最底層結點的層數稱為深度,根節點第一層
葉子結點:沒有子結點的結點
非終端節點:實際上是非葉子結點
度:子結點的個數成為度
二、樹的分類
一般樹:任意一個結點的子結點的個數都不受限制
二叉樹:任意一個結點的子結點個數最多是兩個,且子結點的位置不可更改
二叉數分類:
- 一般二叉數
- 滿二叉樹:在不增加樹層數的前提下,無法再多添加一個結點的二叉樹
- 完全二叉樹:如果只是刪除了滿二叉樹最底層最右邊的連續若干個結點,這樣形成的二叉樹就是完全二叉樹
森林:n個互不相交的樹的集合
三、樹的存儲
二叉樹存儲
連續存儲(完全二叉樹)
優點:查找某個結點的父結點和子結點(也包括判斷有沒有子結點)速度很快
缺點:耗用內存空間過大
鏈式存儲
一般樹存儲
-
雙親表示法:求父結點方便
-
孩子表示法:求子結點方便
-
雙親孩子表示法:求父結點和子結點都很方便
-
二叉樹表示法:把一個一般樹轉化成一個二叉樹來存儲,
- 具體轉換方法:
- 設法保證任意一個結點的左指針域指向它的第一個孩子,右指針域指向它的兄弟,只要能滿足此條件,就可以把一個一般樹轉化為二叉樹
一個普通樹轉換成的二叉樹一定沒有右子樹
森林的存儲
先把森林轉化為二叉樹,再存儲二叉樹
四、樹的遍歷
先序遍歷:根左右
先訪問根結點,再先序訪問左子樹,再先序訪問右子樹
中序遍歷:左根右
中序遍歷左子樹,再訪問根結點,再中序遍歷右子樹
后續遍歷:左右根
后續遍歷左子樹,后續遍歷右子樹,再訪問根節點
五、已知兩種遍歷求原始二叉樹
給定了二叉樹的任何一種遍歷序列,都無法唯一確定相應的二叉樹,但是如果知道了二叉樹的中序遍歷序列和任意的另一種遍歷序列,就可以唯一地確定二叉樹
已知先序和中序求后序
先序:ABCDEFGH
中序:BDCEAFHG
求后序: 這個自己畫個圖體會一下就可以了,非常簡單,這里簡單記錄一下
- 首先根據先序確定根,上面的A就是根
- 中序確定左右,A左邊就是左樹(BDCE),A右邊就是右樹(FHG)
- 再根據先序,A左下面就是B,然后根據中序,B左邊沒有,右邊是DCE
- 再根據先序,B右下是C,根據中序,c左下邊是D,右下邊是E,所以整個左樹就確定了
- 右樹,根據先序,A右下是F,然后根據中序,F的左下沒有,右下是HG,
- 根據先序,F右下為G,然后根據中序,H在G的左邊,所以G的左下邊是H
再來一個例子,和上面的思路是一樣的,這里就不詳細的寫了
先序:ABDGHCEFI
中序:GDHBAECIF
已知中序和后序求先序
中序:BDCEAFHG
后序:DECBHGFA
這個和上面的思路是一樣的,只不過是反過來找,后序找根,中序找左右
樹簡單應用
樹是數據庫中數據組織一種重要形式
操作系統子父進程的關系本身就是一棵樹
面向對象語言中類的繼承關系
哈夫曼樹
六、二叉樹的創建
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
char data;
struct Node * lchild;
struct Node * rchild;
}BTNode;
/*
二叉樹建立
*/
void BuildBT(BTNode ** tree)
{
char ch;
scanf("%c" , &ch); // 輸入數據
if(ch == '#') // 如果這個節點的數據是#說明這個結點為空
*tree = NULL;
else
{
*tree = (BTNode*)malloc(sizeof(BTNode));//申請一個結點的內存
(*tree)->data = ch; // 將數據寫入到結點里面
BuildBT(&(*tree)->lchild); // 遞歸建立左子樹
BuildBT(&(*tree)->rchild); // 遞歸建立右子樹
}
}
/*
二叉樹銷毀
*/
void DestroyBT(BTNode *tree) // 傳入根結點
{
if(tree != NULL)
{
DestroyBT(tree->lchild);
DestroyBT(tree->rchild);
free(tree); // 釋放內存空間
}
}
/*
二叉樹的先序遍歷
*/
void Preorder(BTNode * node)
{
if(node == NULL)
return;
else
{
printf("%c ",node->data );
Preorder(node->lchild);
Preorder(node->rchild);
}
}
/*
二叉樹的中序遍歷
*/
void Inorder(BTNode * node)
{
if(node == NULL)
return;
else
{
Inorder(node->lchild);
printf("%c ",node->data );
Inorder(node->rchild);
}
}
/*
二叉樹的后序遍歷
*/
void Postorder(BTNode * node)
{
if(node == NULL)
return;
else
{
Postorder(node->lchild);
Postorder(node->rchild);
printf("%c ",node->data );
}
}
/*
二叉樹的高度
樹的高度 = max(左子樹高度,右子樹高度) +1
*/
int getHeight(BTNode *node)
{
int Height = 0;
if (node == NULL)
return 0;
else
{
int L_height = getHeight(node->lchild);
int R_height = getHeight(node->rchild);
Height = L_height >= R_height ? L_height +1 : R_height +1;
}
return Height;
}
int main(int argc, char const *argv[])
{
BTNode * BTree; // 定義一個二叉樹
printf("請輸入一顆二叉樹先序序列以#表示空結點:");
BuildBT(&BTree);
printf("先序序列:");
Preorder(BTree);
printf("\n中序序列:");
Inorder(BTree);
printf("\n后序序列:");
Postorder(BTree);
printf("\n樹的高度為:%d" , getHeight(BTree));
return 0;
}
// ABC##DE##F##G##