1 / 55

Data Structure in C ─ 樹狀結構

Data Structure in C ─ 樹狀結構. 大綱. 樹狀結構 ─ 專有名詞 樹狀結構 ─ 表示法 二元樹 二元搜尋樹 引線二元樹 堆積. 樹狀結構 ─專有名詞. 專有名詞 節點( node) 與邊( edge) 祖先( ancestor) 節點與子孫( descendant) 節點 父節點( parent node) 與子節點( children node) 兄弟節點( sibling node) 非終點節點( non-terminal node)

marva
Download Presentation

Data Structure in C ─ 樹狀結構

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. Data Structure in C ─ 樹狀結構

  2. 大綱 • 樹狀結構 ─ 專有名詞 • 樹狀結構 ─ 表示法 • 二元樹 • 二元搜尋樹 • 引線二元樹 • 堆積

  3. 樹狀結構 ─專有名詞 • 專有名詞 • 節點(node)與邊(edge) • 祖先(ancestor)節點與子孫(descendant)節點 • 父節點(parent node)與子節點(children node) • 兄弟節點(sibling node) • 非終點節點(non-terminal node) • 終點節點(terminal node)或樹葉節點(leaf node) • 分支度(degree) • 階度(level) • 高度(path)

  4. A是K的祖先節點,K是A的子孫節點 A是B, C, D的父節點,B, C, D是A的子節點 B, C, D為兄弟節點 J, K, L, G, M, N, I是終點節點,其餘的節點就是非終點節點 A的分支度為3 對B而言,高度為2,深度為1

  5. 樹狀結構 ─專有名詞 (續) • 樹與林(Forest)的關係 • 林是由n >= 0個不同的互斥樹(disjoint trees)所組合而成的,若將樹根移去將形成樹林

  6. 樹狀結構 ─ 表示法 • 圖形表示法 (p. 3) • 串列表示法 • 將圖形化的樹狀結構寫成一個串列 • Ex. 將p.3的圖形轉化為(A(B(E(J), F(K, L)), C(G), D(H(M, N), I)) • 左子 - 右弟表示法 • 每一個節點需要兩個鏈結(或指標)欄位,左欄位連結子節點,右欄位連結兄弟節點  左子-右弟(left child-right sibling)表示法

  7. 樹狀結構 ─ 表示法 (續) • 每一個節點僅有一個最左(left most)子節點及一個近右(closest right)兄弟節點 • 子節點及兄弟節點在樹中的順序並不重要

  8. 樹狀結構 ─ 表示法 (續) • 以分支度為2的樹表示 • 將左子-右弟表示法的樹狀結構順時鐘方向旋轉45度即可

  9. 二元樹 • 二元樹(binary tree) • 二元樹是由節點所組成的有限集合,這個集合若不是空集合就是由樹根及分別是右子樹(right subtree)及左子樹(left subtree) • 特點(與其他一般樹的不同): • 二元樹的節點個數可以是零(一般樹一定要有一個節點) • 二元樹有排列順序的關係(一般樹則沒有) • 二元樹中每一節點的分支度至多為2 (一般樹無限制)

  10. 二元樹 (續) 滿枝二元樹 (fully binary tree) 左斜樹 (left skewed tree) 完整二元樹 (complete binary tree)

  11. 二元樹 (續) • 特性: • 一棵二元樹在第i階度的最多節點數為2i-1,i>=1 • 一棵階度(或深度)為k的二元樹,2k-1,k>=1 • 一棵二元樹,若n0表示所有的樹葉節點,n2表示所有分支度為2的節點,n0=n2+1 • 表示法 • 一維陣列表示法 • 優點:處理容易,若為滿枝二元樹,則相當節省空間 • 缺點:若為歪斜樹,則相當浪費空間;一般而言,較鏈結串列表示法浪費空間

  12. 二元樹 (續)

  13. 鏈結表示法 • 優點:插入與刪除一個節點相當容易 • 缺點:很難找到該節點的父節點(parent) • 解決方式:在節點的結構上增加一欄PARENT,指向其父節點的位置 typedef struct node *tree_pointer; typedef struct node { int data; tree_pointer left_child, right_child; };

  14. 二元樹 (續) • 二元樹追蹤(traversal) • 即走訪樹中的每一個節點,且每個節點恰好被尋訪一次 • 追蹤方式: • 中序追蹤(inorder):先拜訪左子樹(L:向左移動),然後拜訪節點(V:例如,列印資料),再拜訪右子樹(R:向右移動) • 前序追蹤(preorder):先拜訪節點(V),然後拜訪左子樹(L) ,再拜訪右子樹(R) • 後序追蹤(postorder):先拜訪左子樹(L),然後拜訪右子樹(R) ,再拜訪節點(V)

  15. void inorder(Node type *tree) { if (tree != NULL){ inorder(tree->llink); printf(“%d”, tree->data); inorder(tree->rlink); } 中序追蹤:A/B%C*D+E void preorder(Node type *tree) { if (tree != NULL){ printf(“%d”, tree->data); preorder(tree->llink); preorder(tree->rlink); } void postorder(Node type *tree) { if (tree != NULL){ postorder(tree->llink); postorder(tree->rlink); printf(“%d”, tree->data); } 前序追蹤:+*/A%BCDE 後序追蹤:ABC%/D*E+

  16. 將一般的樹轉化為二元樹的方法 • 一般採用左子右弟的表示法,將樹化為二元樹,其步驟如下: • 將節點的所有兄弟節點連接在一起 • 把所有不是連接到最左子點的子節點鏈結刪除 • 順時針旋轉45度

  17. 將樹林轉化為二元樹的方法 • 步驟: • 先將樹林中的每棵樹化為二元樹(不旋轉45度) • 把所有二元樹利用樹根節點全部鏈結在一起 • 順時針旋轉45度

  18. 二元樹 (續) • 算術式的二元樹表示法 • 轉換規則: • 考慮運算子的優先權(priority)與結合性(associativity)適當的予以編號 • 由編號大至小依序處理,最大的為樹根,然後分成兩邊,再依次找各邊最大的為子樹樹根,直到所有的運算子皆處理完畢

  19. 二元樹 (續) • 唯一二元樹的決定 • 給予一對inorder及preorder可以構建出一個唯一的二元樹 • 給予一對inorder及postorder可以構建出一個唯一的二元樹 • 給予一對preorder及postorder則不能構建出一個唯一的二元樹 • 參考pp. 6-70~6-73

  20. 二元搜尋樹 • 二元搜尋樹(binary search tree) • 定義:二元搜尋樹為一二元樹,可以是空集合,假使不是空集合,則樹中的每一節點均含有一鍵值(key value),而且具有下列特性: • 在左子樹的所有節點之鍵值均小於樹根的鍵值 • 在右子樹的所有節點之鍵值均大於樹根的鍵值 • 左子樹和右子樹亦是二元搜尋樹 • 每個鍵值都不一樣 • 當依序給定輸入值(input sequence),我們可以建造一個二元搜尋樹,若使用中序追蹤此樹,可得到由小到大的排序結果

  21. 二元搜尋樹 (續) • 二元搜尋樹的加入 • 當某節點欲加入時,只要逐一比對,依據鍵值的大小往右或往左,即可找到適當位置 加入48 加入90

  22. /* 處理二元搜尋樹,將新增資料加入至二元搜尋樹中 */ void access(char name[], int score) { struct student *node, *prev; if(search(name) != NULL) /* 資料已存在則顯示錯誤 */ { printf("Student %s has existed!\n", name); return; } ptr = (struct student *) malloc(sizeof(struct student)); strcpy(ptr->name, name); ptr->score = score; ptr->llink = ptr->rlink = NULL;

  23. if(root == NULL) /* 當根節點為NULL的狀況 */ root = ptr; else /* 當根節點不為NULL的狀況 */ { node = root; while(node != NULL) /* 搜尋資料插入點 */ { prev = node; if(strcmp(ptr->name, node->name) < 0) node = node->llink; else node = node->rlink; } if(strcmp(ptr->name, prev->name) < 0) prev->llink = ptr; else prev->rlink = ptr; } }

  24. /* 搜尋target所在節點 */ struct student *search(char target[]) { struct student *node; node = root; while(node != NULL) { if(strcmp(target, node->name) == 0) return node; else /* target小於目前節點,往左搜尋 */ if(strcmp(target, node->name) < 0) node = node->llink; else /* target大於目前節點,往右搜尋 */ node = node->rlink; } return node; }

  25. 二元搜尋樹 (續) • 二元搜尋樹的刪除 • 若刪除的是樹葉節點,則直接刪除之 • 若刪除的不是樹葉節點,則在左子樹找一最大的節點或在右子樹找一最小的節點,取代將被刪除的節點

  26. 二元搜尋樹 (續) 取右子樹最小的節點 取左子樹最大的節點

  27. /* 將資料從二元搜尋樹中移除 */ void removing(char name[]) { struct student *del_node; if((del_node = search(name)) == NULL) /* 找不到資料則顯示錯誤 */ { printf("Student %s not found!\n", name); return; } /* 節點不為樹葉節點的狀況 */ if(del_node->llink != NULL || del_node->rlink != NULL) del_node = replace(del_node); else /* 節點為樹葉節點的狀況 */ if(del_node == root) root = NULL; else connect(del_node, 'n'); free(del_node); /* 釋放記憶體 */ printf("Data of student %s deleted!\n", name); }

  28. /* 尋找刪除非樹葉節點的替代節點 */ struct student *replace(struct student *node) { struct student *re_node; /* 當右子樹找不到替代節點,會搜尋左子樹是否存在替代節點 */ if((re_node = search_re_r(node->rlink)) == NULL) re_node = search_re_l(node->llink); if(re_node->rlink != NULL) /* 當替代節點有右子樹存在的狀況 */ connect(re_node, 'r'); else if(re_node->llink != NULL) /* 當替代節點有左子樹存在的狀況 */ connect(re_node, 'l'); else /* 當替代節點為樹葉節點的狀況 */ connect(re_node, 'n'); strcpy(node->name, re_node->name); node->score = re_node->score; return re_node; }

  29. /* 搜尋右子樹替代節點 */ struct student *search_re_r(struct student *node) { struct student *re_node; re_node = node; while(re_node != NULL && re_node->llink != NULL) re_node = re_node->llink; return re_node; } /* 搜尋左子樹替代節點 */ struct student *search_re_l(struct student *node) { struct student *re_node; re_node = node; while(re_node != NULL && re_node->rlink != NULL) re_node = re_node->rlink; return re_node; }

  30. /* 調整二元搜尋樹的鏈結,link為r表示處理右鏈結,為l表處理左鏈結, 為m則將鏈結指向NULL */ void connect(struct student *node, char link) { struct student *parent; parent = search_p(node); /* 搜尋父節點 */ /* 節點為父節點左子樹的狀況 */ if(strcmp(node->name, parent->name) < 0) if(link == 'r') /* link為r */ parent->llink = node->rlink; else if(link == 'l') /* link為l */ parent->llink = node->llink; else /* link為m */ parent->llink = NULL;

  31. else /* 節點為父節點右子樹的狀況 */ if(link == 'r') /* link為r */ parent->rlink = node->rlink; else if(link == 'l') /* link為l */ parent->rlink = node->llink; else /* link為m */ parent->rlink = NULL; }

  32. /* 搜尋node的父節點 */ struct student *search_p(struct student *node) { struct student *parent; parent = root; while(parent != NULL) { if(strcmp(node->name, parent->name) < 0) if(strcmp(node->name, parent->llink->name) == 0) return parent; else parent = parent->llink; else if(strcmp(node->name, parent->rlink->name) == 0) return parent; else parent = parent->rlink; } return NULL; }

  33. 二元搜尋樹 (續) • 利用二元搜尋樹來排序一組資料 • 步驟: • 先將輸入資料置於一個queue中 • 以第一個資料當做二元樹的樹根 • 以後的資料與樹根比較,若小於則成為樹根之左子樹,若大於則為其右子樹,如此一直遞迴式的進行 • 建立完畢後再以中序法追蹤 • 若要由小而大排序,則將結果直接輸出。若是由大而小,則先將結果置於stack中,以後再相反輸出

  34. 引線二元樹 • 引線二元樹(threaded binary tree) • 一般二元樹中有一半以上的link field是null link,為便利儲存及節省link欄位的浪費,將空的link換成一種叫引線(thread)的指標 • 引線二元樹的資料結構 1. 當LBIT=1時,LLINK是正常指標 2. 當LBIT=0時,LLINK是引線 3. 當RBIT=1時,RLINK是正常指標 4. 當RBIT=0時,RLINK是引線

  35. 開頭節點 (不放置任何資料)

  36. 引線二元樹 (續) • 特性: • 引線樹在做中序追蹤時不必使用stack,對任何一個節點平均只執行一次 • 在引線樹中毋需追蹤整個樹,可以由任何節點找到它中序次序的前一個或後一個節點 • 對於加入或刪除一個節點則引線樹較慢,這是因為牽涉到引線的重排

  37. 引線二元樹 (續) • 引線二元樹的加入

  38. void insert_right( struct tbintree *node_parent,struct tbintree *node) { struct tbintree *w; node->rchild = node_parent->rchild; node->rbit = node_parent->rbit; node->lchild = node_parent; node->lbit = 0; node_parent->rchild = node; node_parent->rbit = 1; if ( node->rbit == 1 ) /*node底下還有tree*/ { w = insucc( node ); w->lchild = node; } } 加入新節點於某節點的右方

  39. void insert_left( struct tbintree *node_parent,struct tbintree *node) { struct tbintree *w; node->lchild = node_parent->lchild; node->lbit = node_parent->lbit; node->rchild = node_parent; node->rbit = 0; node_parent->lchild = node; node_parent->lbit = 1; if ( node->lbit == 1 ) /*node 底下還有tree*/ { w = inpred( node ); w->rchild = node; } } 加入新節點於某節點的左方

  40. 引線二元樹的刪除

  41. if ( ptr->lbit == 0 && ptr->rbit == 0 ) { if ( ptr_parent == root ) /*刪除第一個節點*/ { ptr_parent->lchild = root; ptr_parent->lbit = 0; } /*刪除左節點*/ else if ( ptr->number < ptr_parent->number ) { ptr_parent->lchild = ptr->lchild; ptr_parent->lbit = 0; } else /*刪除右節點*/ { ptr_parent->rchild = ptr->rchild; ptr_parent->rbit = 0; } free(ptr); } 刪除節點於樹葉節點的左右方

  42. else if ( ptr->lbit == 1 && ptr->rbit == 1 ) { /*求ptr的前行者節點,將右子樹插入前行者右方*/ ptr_pred = inpred ( ptr); ptr_pred->rchild = ptr->rchild; ptr_pred->rbit = ptr->rbit; ptr_parent->lchild = ptr->lchild; free(ptr); } else /*刪除一分支度節點*/ { if ( ptr_parent == root ) /*刪除第一節點*/ { if ( ptr->lbit == 1 ) { ptr_pred = inpred(ptr); root->lchild = ptr->lchild; ptr_pred->rchild = root; } else { ptr_succ = insucc(ptr); root->lchild = ptr->rchild; ptr_succ->lchild = root; } }

  43. else { if ( ptr->number < ptr_parent->number ) ptr_parent->lchild = ptr->lchild; else ptr_parent->rchild = ptr->rchild; } } 刪除節點於非樹葉節點的左右方

  44. 堆積 • 堆積(heap) • 定義:堆積是一棵二元樹,其樹根的鍵值大於子樹的鍵值,且必須符合完整二元樹 • 不管左子樹和右子樹的大小順序(與二元搜尋樹最大的差異) • Heap可用於排序上,簡稱Heap Sort • 在一堆雜亂無章的資料中,利用heap sort將它由小至大或由大至小排序皆可 • 首先,將一堆資料利用完整二元樹將其建立起來,再將它調整為Heap,爾後再依題意用Stack(由大到小)或Queue(由小至大)輔助之

  45. Heap的調整 • 由上而下 • 從樹根開始到 ,分別與其子節點相比,若前者大則不用交換。反之,則要交換 • 讓子節點先比,找出最大者再與父節點比

  46. 堆積 (續) • Heap的加入

  47. void insert_f(void) { int id_temp; if(last_index >= MAX) /* 資料數超過上限,顯示錯誤訊息 */ { printf("\n Login members are more than %d!!\n", MAX); printf(" Please wait for a minute!!\n"); } else { printf("\n Please enter login ID number: "); scanf("%d", &id_temp); create(id_temp); /* 建立堆積 */ printf(" Login successfully!!\n"); } } void create(int id_temp) /* ID_TEMP為新增資料 */ { heap_tree[++last_index] = id_temp; /* 將資料新增於最後 */ adjust_u(heap_tree, last_index); /* 調整新增資料 */ }

  48. void adjust_u(int temp[], int index) /* INDEX為目前資料在陣列之INDEX */ { while(index > 1) /* 將資料往上調整至根為止 */ { if(temp[index] <= temp[index/2]) /* 資料調整完畢就跳出,否則交換資料 */ break; else exchange(&temp[index], &temp[index/2]); index /= 2; } } void exchange(int *id1, int *id2) /* 交換傳來之ID1及ID2儲存之資料 */ { int id_temp; id_temp = *id1; *id1 = *id2; *id2 = id_temp; }

More Related