資料結構-樹(基本概念整合)
樹是最優美的形狀(世間萬物皆有其樹結構)
①樹的基本概念
樹(Tree)是n(n>=0)個結點的有限集。
在任意一棵非空樹中:
(1)有且僅有一個特定的稱為根(Root)的結點;
(2)當n>1時,其餘結點可分為m(m>0)個互不相交的有限集T1,T2,...,Tn,其中每個集合本身又是一棵樹,並稱為根的子樹(SubTree)
層次 樹
1 A
/ | \
2 B C D
/ \ | / | \
3 E F G H I J
/ \ |
4 K L M
★樹的結點包含一個數據元素及若干指向其子樹的分支。
(1)度
結點擁有的子樹樹稱為結點的度(Degree)如:上圖A的度為3,C的度為1,F的度為0。
度為0的結點稱為葉子(Leaf)或終端結點,如:上圖K,L,F,G,M,I,J都是樹的葉子
度不為0的結點稱為非終端結點或分支結點,如:上圖A,B,C,D,E,H
★樹的度是樹內各節點的度的最大值,如:上圖的樹的度為 3 。
(2)結點(家譜圖)
結點的子樹的根稱為該結點的孩子(Child),相應地,該結點稱為孩子的雙親(Parent)如:上圖D是A的孩子,A是D的雙親。
同一個雙親的孩子叫兄弟(Sibling)如:上圖H,I,J為互為兄弟
其雙親在同一層的結點互為堂兄弟。如上圖G與E、F、H、I、J互為堂兄弟
結點的祖先是從根到該結點所經分支上的所有結點。如:上圖M的祖先為A、D、H
(3)層次,深度(你家幾代同堂啊?)
結點的層次(Level)從根開始定義起,根為第一層,根的孩子為第二層,
樹中結點的最大層次成為樹的深度(Depth)或高度。如:上圖樹的深度為4
(4)有序樹與無序樹(長子,次子。。)
樹中結點的各子樹看成從左至右是有次序的(即不能互換),則稱該樹為有序樹
有序樹中最左的子樹的根稱為第一個孩子,最右邊的稱為最後一個孩子。(畢竟有序,排好了誰是老大,誰是老二,長兄有序嘛)
★森林(Forest)是m(m>=0)棵互不相交的樹的集合。對樹中每個結點而言,其子樹的集合即為森林。
由此也可以森林和樹相互遞迴的定義來描述樹。
②二叉樹
1、二叉樹的定義及其主要特徵
二叉樹(Binary Tree)是另一種樹形結構,
★二叉樹的特點是:(1)每個結點至多隻有兩棵子樹(即二叉樹中不存在度大於2的結點),
(2)二叉樹的子樹有左右之分,其次序不能隨意顛倒。
二叉樹要麼為空,要麼是由一個根結點加上兩棵分別稱為左子樹和右子樹的,互不相交的二叉樹組成。
★二叉樹的五種形態
| | A | A | A
Φ | A | B | B C | B
(1) | (2) | (3) | (4) | (5)
★二叉樹的性質
(性質一)在二叉樹的第 i 層上至多有 2^(i-1)個結點 (i >=1)
第一層 :1(2^0) 第二層:2(2^1) 第三層:4(2^2) 第四層:8(2^3)
(性質二)深度為 k 的二叉樹至多有2^k -1個結點(k>=1)
由性質一得:第k層,2^k - 1 個結點
1+2+2^2+....+2^(k-1) = ( 1 - 2^k)/(1-2) = 2^k - 1
(性質三)★對任何一棵二叉樹 T ,如果其終端結點數位n0,度為2的結點數位n2,則n0=n2+1。
式一: n = n0 + n1 + n2 (結點總數 等於 度為0 加 度為1 加 度為2)
式二: n = n0 + 2*n2 +1(n = 分支總數+1 ;分支總數 = n1+n2 (分支是由度為1,度為2的結點射出的))
式二 - 式一得: n0 = n2 + 1
★完全二叉樹
一棵深度為 k 且有2^k - 1個結點的二叉樹為滿二叉樹
(性質一)具有n個結點的完全二叉樹的深度為 」 log 2 n」+1
由普通的二叉樹性質二得:深度為k,結點數最多2^k - 1個,那麼log一下就出來了
(性質二)如果對一棵有n個結點的完全二叉樹(其深度為」 log 2 n」+1 )的結點按層序編號(從第一層到最後一層,每層從左到右),對任一結點i(1<= i <=n)有
(1)如果 i =1,則結點 i 是二叉樹的根,無雙親;
如果 i >1,則其雙親Parent( i ) 是結點 」i / 2」
(2)如果2*i >n,則結點 i 無左孩子(結點 i 為葉子節點) ;否則其左孩子LChild( i )是結點2*i。
(3)如果2*i+1>n,則結點 i 無右孩子;否則其右孩子RChild( i )是結點2*i+1。
【這個性質,對於建立二叉樹,有著非凡的意義】
且看下面這個完全二叉樹:
1
/ \
2 3 [ i / 2 ]
/ \ / \ / \
4 5 6 7 i i+1
/ \ / \ / \ / \ / \ / \
8 9 10 11 12 13 14 15 2 i 2 i+1 2 i+2 2 i+3
【性質二通俗版】從下往上看:結點1是根,沒雙親;其他點的雙親是 i / 2 取下值
從上往下看:左孩子 是根節點的兩倍(偶數),右孩子是根節點的兩倍+1(奇數)
2、二叉樹的順序儲存結構和鏈式儲存結構
順序儲存結構:
#define MAX_TREE_SIZE 100 //二叉樹最大結點數
typedef TElemTyle SqBiTree[MAX_TREE_SIZE]; //0號單元儲存根節點
SqBiTree bt;
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12]
[1] [2] [3] [4] [5] [0] [0] [0] [0] [6] [7] [8]
鏈式儲存結構
typedef struct BiTNode{
TElemType date;
struct BiTNode *lchild,*rchild;//左右孩子指標
}BiTNode,*BiTree;
[ ] [A] [^]/
[ ] [B] [ ]
/ \
[^] [C] [^] [ ] [D] [ ]
/ \
[^] [E] [ ] [^] [F] [^]
\
[^] [G] [^]
——————————————————————————————————————————————
不同的建二叉樹技巧:
——————————————————————————————————————————————
3、二叉樹的遍歷:傳送門(建樹,前序,中序,後序,中序非遞迴,深度,葉子數,節點數)
(1)先序遍歷二叉樹 (根>左>右)
(2)中序遍歷二叉樹 (左>根>右)
(3)後序遍歷二叉樹 (左>右>根)
(4)層次遍歷二叉樹
4、線索二叉樹的基本概念和構造(傳送門)
[ lchild ] [ LTag ] [ data ] [ RTag ] [ rchild ]
LTag = { 0 : lchild 域指示結點的左孩子 1 : lchild 域指示結點的前驅 }
RTag = { 0 : rchild 域指示結點的右孩子 2 : rchild 域指示結點的後繼 }
以這種結點結構構成的二叉連結串列作為二叉樹的儲存結構,叫做線索連結串列,其中,指向結點前驅和後繼的指標,叫做線索。
加上線索的二叉樹叫做線索二叉樹(Threaded Binary Tree)
對二叉樹以某種次序遍歷使其變成線索二叉樹的過程叫做線索化
(1)前序線索二叉樹
(2)中序線索二叉樹
(3)後序線索二叉樹
③樹、森林
1、樹的儲存結構
A
↙ ↘
B C
↙ ↙ ↘
D E F
↙ ↓ ↘ ↘
G H I J
一、雙親表示法(自下而上)
我們假設以一組連續空間儲存樹的結點,同時在每個結點中,附設一個指示器指示其雙親結點在陣列中的位置。
每個結點:【data】【parent】
其中data:資料域,儲存結點的資料資訊。parent:指標域,儲存該結點的雙親在陣列找那個的下標。
#define MAX_TREE_SIZE 100
typedef int TElemType; //樹節點的資料型別
typedef struct PTNode{
TElemType data; //結點資料
int parent; //雙親位置
}PTNode;
typedef struct{
PTNode nodes[MAX_TREE_SIZE]; //結點陣列
int r,n; //根的位置和結點數
}PTree;
鏈式:typedef struct PTrNode{
TElemType data; //結點資料
struct PTrNode *parent; //指向雙親的指標
}PTrNode;
下標 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
data | A | B | C | D | E | F | G | H | I | J |
parent | -1 | 0 | 0 | 1 | 2 | 2 | 3 | 3 | 3 | 4 |
二、孩子表示法
樹中每個結點可能有多課子樹,可以考慮用多重連結串列,即每個結點有多個指標域,其中每個指標指向一棵子樹的根節點,這種方法叫做多重連結串列表示法。
方案一:
每個結點【data】【child1】【child2】【child3】....【childd】
其中data是資料域,child1~childd是指標域,用來指向該結點的孩子結點。指標個數為樹的度數(每個結點的孩子結點個數為樹的度,即最多大的孩子結點個數)
【缺點:這樣很浪費空間】
方案二:
每個結點【data】【degree】【child1】【child2】【child3】....【childd】
其中data是資料域,degree是度域,child1~childd是指標域,用來指向該結點的孩子結點。
【這樣就能減少空指標的浪費】
方案三:
用兩種結點結構:
表頭陣列的表頭節點:【data】【firstchild】其中:data是資料域,儲存某結點的資料資訊。firstchild是頭指標域,儲存孩子連結串列的頭指標。
孩子連結串列的孩子結點:【child】【next】其中:child是資料域,儲存某個節點在表頭陣列中的下標,next是指標域,指向下一個孩子結點的指標。
#define MAX_TREE_SIZE 100
typedef int TElemType; //樹節點的資料型別
typedef struct CTNode{ //孩子結點
int child;
struct CTNode *next;
}*ChildPrt;
typedef struct{ //表頭結構
TElemType data;
ChildPtr firstchild;
}CTBox;
typedef struct{ //樹結構
CTBox nodes[MAX_TREE_SIZE]; //結點陣列
int r,n; //根的位置和結點數
}CTree;
下標 data firstchild child next 0 【A】 【 】 → → → 【 1 】 【 】 → → → 【 2 】 【 ^ 】
1 【B】 【 】 → → → 【 3 】 【 ^ 】
2 【C】 【 】 → → → 【 4 】 【 】 → → → 【 5 】 【 ^ 】
3 【D】 【 】 → → → 【 3 】 【 】 → → → 【 7 】 【 】 → → 【 8 】 【 ^ 】
4 【E】 【 】 → → → 【 9 】 【 ^ 】
5 【F】 【 * 】
6 【G】 【 * 】
7 【H】 【 * 】
8 【I 】 【 * 】
9 【J】 【 * 】
雙親孩子表示法:(結合上面兩種表示法)
下標 data parent firstchild child next
0 【A】 【 -1 】 【 】 → → → 【 1 】 【 】 → → → 【 2 】 【 ^ 】
1 【B】 【 0 】 【 】 → → → 【 3 】 【 ^ 】
2 【C】 【 0 】 【 】 → → → 【 4 】 【 】 → → → 【 5 】 【 ^ 】
3 【D】 【 1 】 【 】 → → → 【 3 】 【 】 → → → 【 7 】 【 】 → → 【 8 】 【 ^ 】
4 【E】 【 2 】 【 】 → → → 【 9 】 【 ^ 】
5 【F】 【 2 】 【 * 】
6 【G】 【 3 】 【 * 】
7 【H】 【 3 】 【 * 】
8 【I 】 【 3 】 【 * 】
9 【J】 【 4 】 【 * 】
三、孩子兄弟表示法
結點結構:【data】【firstchild】【rightsib】
其中data為資料域,firstchild為指標域,儲存該節點的第一個孩子結點儲存地址,rightsub是指標域,儲存該節點的右兄弟結點的儲存地址。
typedef int TElemType; //樹節點的資料型別
typedef struct CSNode{
TElemType data;
struct CSNode * firstchild,*rightsib;
}CSNode , *CSTree;
【 A 】【 】【 ^ 】↓
【 B 】【 】【 ^ 】 → → → → → → → → → → 【 C 】【 】【 ^ 】
↓ ↓
【 D 】【 】【 ^ 】 【 E 】【 】【 】 → 【 F 】【 ^ 】【 ^ 】
↓ ↓
【 G 】【 ^ 】【 】 → 【 H 】【 ^ 】【 】 → 【 I 】【 ^ 】【 ^ 】 【 J 】【 ^ 】【 ^ 】
斜著看,他其實是一棵二叉樹。(根據程式碼也可以看出來)
根據二叉樹的特性來處理樹
2、森林和二叉樹的轉換
一、森林轉換成二叉樹
如果F={ T1,T2,....,Tm }是森林,則可按如下規則轉換成一棵二叉樹B=(root,LB,RB)
(1)若F為空,即m=0,則B為空樹。
(2)若F非空,即m≠0,則B的根root即為森林中第一棵樹的根ROOT(T1);
B的左子樹LB是從T1中根結點的子樹森林F1= { T11,T12,...,T1m1} 轉換而成的二叉樹
B的右子樹RB是從森林F‘ = { T2,T3,.....,Tm}轉換而來的二叉樹。
二、二叉樹轉換成森林
如果B=(root,LB,RB)是一棵二叉樹,則可按如下規則轉換成森林F={ T1,T2,....,Tm }
(1)若B為空,則F為空。
(2)若B非空,則F中第一棵樹T1的根root即為二叉樹B的根root;
T1中根結點的子樹森林F1是由B的左子樹LB轉換而成的森林;
F中除T1之外其餘樹組成的森林F‘ = { T2,T3,.....,Tm}是由B的右子樹RB轉換而成的森林。
3、樹與森林的遍歷
一、先序遍歷森林
若森林非空,則可按下述規則遍歷之;
(1)訪問森林中第一棵樹的根結點;
(2)先序遍歷第一棵樹中根結點的子樹森林;
(3)先序遍歷除去第一棵樹之後剩餘的樹構成的森林。
二、中序遍歷森林
若森林非空,則可按下述規律遍歷之;
(1)中序遍歷森林中第一棵樹的根結點的子樹森林;
(2)訪問第一棵樹的根結點;
(3)中序遍歷除去第一棵樹之後剩餘的樹構成的森林。
————————————————————
特別的建樹技巧
【並查集建樹(利用陣列的一對多特性)雙親表示法】
————————————————————
④樹與二叉樹的應用
1、二叉排序樹
2、平衡二叉樹
3、哈夫曼樹和哈夫曼編碼。