1. 程式人生 > >資料結構-樹(基本概念整合)

資料結構-樹(基本概念整合)

樹是最優美的形狀(世間萬物皆有其樹結構)

①樹的基本概念

(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、哈夫曼樹和哈夫曼編碼。