1 / 48

資料結構

資料結構. 第 6 章 樹狀結構. 6-1  認識樹 . 樹 (tree) 是由一或多個節點 所組成的有限集合,其特質如下: 有一個特殊節點,稱為樹根 。 其餘節點可以分為 n 個互斥集合 T 1 、 T 2 、 … 、 T n (n ≥ 0) ,而且每個集合也都是一棵樹 。. 6-1-1  樹的相關名詞 . 分支度 (degree) 終端節點 (terminal node) 非終端節點 (nonterminal node) 階度 (level) 父節點 (parent) 子節點 (child) 兄弟 (sibling)

muriel
Download Presentation

資料結構

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 資料結構 第6章 樹狀結構

  2. 6-1 認識樹 • 樹 (tree) 是由一或多個節點 所組成的有限集合,其特質如下: • 有一個特殊節點,稱為樹根 。 • 其餘節點可以分為n個互斥集合T1、T2、…、Tn (n ≥ 0),而且每個集合也都是一棵樹 。

  3. 6-1-1 樹的相關名詞 • 分支度 (degree) • 終端節點 (terminal node) • 非終端節點 (nonterminal node) • 階度 (level) • 父節點 (parent) • 子節點 (child) • 兄弟 (sibling) • 祖先 (ancestor) • 子孫 (descendant) • 高度 (height) 或深度 (depth) • 樹林 (forest)

  4. 6-1-2 樹的表示方式 • 我們可以使用串列表示樹,例如右圖的樹可以表示成(A(B(E, F(K)), C(G, H(L, M(N)), I), D(J))) • 至於每個節點在記憶體的儲存方式,我們可以使用如下的結構 :

  5. 6-2 二元樹 • 二元樹(binary tree) 是每個節點最多有兩個子節點的樹,節點的左邊稱為左子樹 ,節點的右邊稱為右子樹 。

  6. 範例6.2:證明二元樹第i階的最多節點個數為2i-1,i ≥ 1。 • 解答: • 當i = 1時,第1階最多只有樹根一個節點,故2i-1 = 21-1 = 20 = 1成立。 • 假設當1 ≤ i < k時,第i階的最多節點個數為2i-1成立,故第k - 1階的最多節點個數為2k-2。 • 證明當i = k時,第i階的最多節點個數為2i-1亦成立。由於第k - 1階的最多節點個數為2k-2,而二元樹的每個節點最多有兩個子節點,故第k階的最多節點個數為2k-2x 2 = 2k-1。

  7. 範例6.3:證明高度為h之二元樹的最多節點個數為2h - 1,h ≥ 1。 • 解答:由於二元樹第i階的最多節點個數為2i-1,因此,對高度為h的二元樹來說,全部存滿的話,總共有20 + 21 + … + 2h-1 = 2h - 1個節點。

  8. 6-2-1 完滿二元樹V.S. 完整二元樹 • 完滿二元樹 (full binary tree) • 完整二元樹 (complete binary tree)

  9. 假設完整二元樹的節點個數為n且節點依照0 ~ n - 1的順序編號,則其任意節點i (0 ≤ i ≤ n - 1) 具有下列特質: • 若i = 0,表示節點i為樹根,否則節點i的父節點為 (i - 1)/2 。 • 若2i + 1 ≥ n,表示節點i沒有左子節點,否則節點i的左子節點為2i + 1。 • 若2i + 2 ≥ n,表示節點i沒有右子節點,否則節點i的右子節點為2i + 2。

  10. 範例6.6:證明節點個數為n之完整二元樹的高度為 log2n + 1。

  11. 6-2-2 二元樹的表示方式 • 使用陣列實作二元樹

  12. 使用鏈結串列實作二元樹 typedef struct node{ struct node *lchild; char data; struct node *rchild; }tree_node; typedef tree_node *tree_pointer;

  13. 6-2-3 將一般樹轉換為二元樹 • 範例6.7:將下面的一般樹轉換為二元樹。

  14. 6-3 二元樹的走訪 6-3-1 中序走訪 inorder(tree_pointer root) { if (root){ inorder(root->lchild); printf("%d ", root->data); inorder(root->rchild); } } 右圖的中序走訪的結果為DBGEHACFI。

  15. 6-3-2 前序走訪 preorder(tree_pointer root) { if (root){ printf("%d ", root->data); preorder(root->lchild); preorder(root->rchild); } } 右圖的前序走訪的結果為ABDEGHCFI。

  16. 6-3-3 後序走訪 postorder(tree_pointer root) { if (root){ postorder(root->lchild); postorder(root->rchild); printf("%d", root->data); } } 右圖的後序走訪的結果為DGHEBIFCA。

  17. 6-3-4 決定唯一的二元樹 • 範例6.11:[決定唯一的二元樹] 假設二元樹的中序與前序走訪分別為DBEACGFH、ABDECFGH,試據此推算出該二元樹。

  18. 6-4 二元樹的其它運算 6-4-1 複製二元樹 範例6.13:撰寫一個函數實作複製二元樹運算。 tree_pointer copy(tree_pointer source) { tree_pointer destination; if (source){ destination = (tree_pointer)malloc(sizeof(tree_node)); destination->lchild = copy(source->lchild); destination->rchild = copy(source->rchild); destination->data = source->data; return destination; } return NULL; }

  19. 6-4-2 比較二元樹 範例6.14:撰寫一個函數實作比較二元樹運算。 int equal(tree_pointer source, tree_pointer destination) { if ((!source && !destination) || (source && destination && (source->data == destination->data) && equal(source->lchild, destination->lchild) && equal(source->rchild, destination->rchild)) return 1; else return 0; }

  20. 6-5 運算式樹 • 運算式樹 (expression tree) 指的是滿足下列條件的二元樹: • 終端節點為運算元 (operand),而非終端節點為運算子 (operator)。 • 子樹為子運算式且其樹根為運算子。

  21. 6-6 引線二元樹 • 引線二元樹 (threaded binary tree) 的左引線 (left thread) 指向節點本身的中序前行者 (inorder predecessor),右引線 (left thread) 指向節點本身的中序後繼者 (inorder successor) 。

  22. 6-6-1 節點結構 #define THRAED 1 #define POINTER 0 typedef struct thnode{ int lthread; struct threaded_node *lchild; char data; struct threaded_node *rchild; int rthread; }threaded_node; typedef threaded_node *threaded_pointer;

  23. 空的引線二元樹 (只有包含首節點) 加入首節點的引線二元樹

  24. 6-6-2 中序走訪 • 範例6.15:撰寫一個函數在引線二元樹中找出任意節點的中序後繼者。 threaded_pointer inorder_successor(threaded_pointer ptr) { threaded_pointer tmp; tmp = ptr->rchild; if (tmp == POINTER) while(tmp->lchild == POINTER) tmp = tmp->lchild; return tmp; }

  25. 範例6.16:利用範例6.15所撰寫的inorder_successor() 函數完成引線二元樹的中序走訪。 threaded_inorder (threaded_pointer head) { threaded_pointer tmp; for(;;){ tmp = inorder_successor(tmp); if (tmp == head) break; printf("%d ", tmp->data); } }

  26. 6-6-3 插入節點 • 情況一: • 將新節點T插入節點S的右兒子且新節點T是樹葉

  27. threaded_pointer tmp; T->rthread = S->rthread; T->rchild = S->rchild; T->lthread = THREAD; T->lchild = S; S->rthread = POINTER; S-rchild = T; if (T->rthread == POINTER){ tmp = T->rchild; while(tmp->lchild == POINTER) tmp = tmp->lchild; tmp->lchild = T; } (1) (2) (3) (4) • 將新節點T插入節點S的右兒子且新節點T不是樹葉

  28. 情況二: • 將新節點T插入節點S的左兒子且新節點T是樹葉

  29. T->lthread = S->lthread; T->lchild = S->lchild; T->rthread = THREAD; T->rchild = S; S->lthread = POINTER; S-lchild = T; if (T->lthread == POINTER){ tmp = T->lchild; while(tmp->rchild == POINTER) tmp = tmp->rchild; tmp->rchild = T; } (1) (2) (3) (4) • 將新節點T插入節點S的左兒子且新節點T不是樹葉

  30. 6-7 二元搜尋樹 • 二元搜尋樹是滿足下列條件的二元樹: • 每個節點包含唯一的鍵值 (key)。 • 左右子樹亦為二元搜尋樹。 • 左子樹的鍵值必須小於其樹根的鍵值 • 右子樹的鍵值必須大於其樹根的鍵值。

  31. 6-7-1 搜尋節點 • 先和樹根做比較,若指定的鍵值小於樹根,就以同樣的方式搜尋樹根的左子樹,若指定的鍵值大於樹根,就以同樣的方式搜尋樹根的右子樹,左右子樹均據此定義遞迴地搜尋下去。 • 寫出在右圖的二元搜尋樹搜尋70和35兩個鍵值的過程。

  32. 範例6.20:以遞迴的方式撰寫一個函數在二元搜尋樹搜尋節點。範例6.20:以遞迴的方式撰寫一個函數在二元搜尋樹搜尋節點。 tree_pointer search2(tree_pointer root, int key) { if (!root) return NULL; if (key == root->data) return root; if (key < root->data) return search2(root->lchild, key); else return search2(root->rchild, key); }

  33. 6-7-2 插入節點 • 先和樹根做比較,若新節點小於樹根,就以同樣的方式移往樹根的左子樹,若新節點大於樹根,就以同樣的方式移往樹根的右子樹,直到抵達子樹的尾端,再將新節點加入子樹的尾端。

  34. 範例6.21:[建構二元搜尋樹] 將數字串列 (25, 30, 24, 58, 45, 26, 12, 14) 建構為二元搜尋樹。

  35. 假設將數字串列 (25, 30, 24, 58, 45, 26, 12, 14) 變更為 (25, 30, 24, 58, 45, 26, 14, 12),後面兩個資料的順序交換,則建構出來的二元搜尋樹如下:

  36. 假設將數字串列 (25, 30, 24, 58, 45, 26, 12, 14) 變更為 (12, 14, 24, 25, 26, 30, 45, 58),資料由小到大排序,則建構出來的二元搜尋樹如下:

  37. 6-7-3 刪除節點 • 情況一:當欲刪除的節點是樹葉時,直接刪除該節點即可。

  38. 情況二:當欲刪除的節點有一棵子樹時,將其父節點的指標改指向其子節點,然後刪除該節點即可。

  39. 情況三:當欲刪除的節點有兩棵子樹時,以該節點的左子樹中最大節點或右子樹中最小節點填入其位置,然後刪除該節點即可。

  40. 6-8 霍夫曼樹 • 範例6.23:[建構霍夫曼樹] 假設A、B、C、D、E、F等六個符號的出現頻率為0.2、0.15、0.3、0.18、0.05、0.12,試據此建構霍夫曼樹並寫出各個符號的編碼。

  41. 範例6.24:[霍夫曼碼解碼] 根據範例6.23所設計的霍夫曼碼,將111110010000110進行解碼。 • 解答:由於A、B、C、D、E、F等六個符號分別被編碼為01、110、10、00、1110、1111,故將111110010000110解碼後會得到FCADDB。

  42. 6-9 樹林 • 樹林 (forest) 是由n棵互斥樹所組成的集合 (n ≥ 0),適合用來表示互斥集合 (disjoin set)。 樹去掉樹根後就會得到樹林 樹

  43. 範例6.26:將下面的樹林轉換為二元樹。

  44. 6-10 集合 • 集合 (set) 是一群相同類型、沒有順序之分的元素,而互斥集合 (disjoin set) 是沒有相同元素的集合 。

  45. 互斥集合常見的運算有下列兩種: • 聯集 (union) • 搜尋 (find) (a) S1 U S2 (b) S2 U S1

  46. 使用陣列存放互斥集合,以下圖的S1、S2、S3等三個集合為例,陣列的內容如下,

  47. 互斥集合的聯集運算函數 set_union(int i, int j) { parent[i] = j; } • 呼叫聯集運算函數建立S1、S2、S3等三個集合和S1∪S2集合: set_union(0, 2); set_union(1, 2); set_union(3, 4); set_union(5, 8); set_union(6, 8); set_union(7, 8); set_union(4, 2);

  48. 互斥集合的搜尋運算函數 int find(int i) { while(parent[i] > 0 i = parent[i]; return i; }

More Related