第六章 二元搜尋樹 現在我們的注意焦點要轉到搜尋樹(search tree)了,要深度討論兩種標準的樹結構(tree structure),就是本章所要說明的二元搜尋樹(binary search tree)以及下一章所要討論的 AVL 平衡樹(AVL tree)。這兩種樹其資料都依序排列的,它們之間的差別只在於.

Slides:



Advertisements
Similar presentations
第一單元 建立java 程式.
Advertisements

第7章 樹與二元樹 (Trees and Binary Trees)
第6章 二叉树和树 前面的章节主要讨论的是线性结构,二叉树和树属于非线性的结构。遍历非线性结构比线性结构要麻烦。
第三章 鏈結串列 Linked List.
陳維魁 博士 儒林圖書公司 第九章 資料抽象化 陳維魁 博士 儒林圖書公司.
File Access 井民全製作.
Tree(樹) 什麼是「樹」? 「樹」的範例 「樹」的定義 「樹」的表示法.
資料結構 第3章 鏈結串列.
結構(struct).
第十一章 結構.
struct 可以在同一個名稱下擁有多種資料型態。使用struct能讓資料的存取和處理更為靈活。
Visual C++ introduction
資料結構設計與C++程式應用 Fundamentals of Data Structures and Their Applications Using C++ 第3章 佇列 資料結構設計與C++程式應用.
补充内容 结构体 概述 定义结构体类型和定义结构体变量 结构体变量的引用 结构体变量的初始化 指针与结构体 用typedef定义类型的别名.
2 C++ 程式概論 2.1 C++ 程式結構 程式註解 // 插入標題檔 #include 2-3
列舉(enum).
String C語言-字串.
第七章 AVL搜尋樹 二元搜尋樹簡單易懂,不過有一個問題:它並非平衡樹。本章將介紹平衡的 AVL 搜尋樹,討論它的資料結構、函式,並設計程式使用它。
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
101北一女中 資訊選手培訓營 妳不可不了解的指標 Nan.
第12章 樹狀搜尋結構 (Search Trees)
類別(class) 類別class與物件object.
SQL Stored Procedure SQL 預存程序.
(Circular Linked Lists)
第 七 章 樹狀結構 課程名稱:資料結構 授課老師:________ 2019/1/1.
Chapter 9 高度平衡二元搜尋樹 AVL Binary Search Tree
Java 程式設計 講師:FrankLin.
樹狀結構 Tree Structure chapter 7 德明科技大學資訊科技系.
JAVA 程式設計與資料結構 第四章 陣列、字串與數學物件.
Chap3 Linked List 鏈結串列.
Chapter 11 B-tree 11.1 m-way 搜尋樹 11.2 B-tree.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
樹 2 Michael Tsai 2013/3/26.
程式設計實習課(四) ----C 函數運用----
第一單元 建立java 程式.
Chap4 Tree.
Ch20. 計算器 (Mac 版本).
資料結構 優點 缺點 1 陣列 (Array) 沒有額外變量 (例head, next,...) 運作/操作較簡單 更新資料時,若要保持順序,需要移動較大量資料 靜態結構Static (宣告時已決定了陣列元素多少,不能在程式執行期間增減元素) 2 隊列Queue (FIFO) 容易更新 加入enqueue:
自我參考結構 (self-reference – 1)
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
網路安全管理報告 緩衝區溢位攻擊 學生:吳忠祐 指導教授:梁明章.
Reference to FUNDAMENTALS OF DATA STRUCTURE IN C++
輸入&輸出 函數 P20~P21.
第7章 樹與二元樹(Trees and Binary Trees)
挑戰C++程式語言 ──第8章 進一步談字元與字串
資料結構與C++程式設計進階 樹狀結構(Tree) 講師:林業峻 CSIE, NTU 11/ 12, 2009.
資料結構使用Java 第9章 樹(Tree).
樣版.
C qsort.
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第八章 堆 積 堆積(heap)是樹結構的第三種型態。堆積是一棵二元樹,其左右子樹節點的值均較其父母節點的值小。堆積的根節點值保證是該樹最大值。這中堆績稱為最大堆績。堆積的子樹可擺在左邊當左子樹,也可擺在右邊當右子樹,因此左右子樹俱有相同的性質。 堆積還有一個有趣的特性,就是以陣列實作較以連結串列實作佳。當以陣列實作堆積時,已知父母節點的註標(subscript)就可算出其左右子樹節點的註標,反之亦然。
講師:郭育倫 第3章 基本資料結構 講師:郭育倫
挑戰C++程式語言 ──第7章 輸入與輸出.
流程控制:Switch-Case 94學年度第一學期‧資訊教育 東海大學物理系.
第14章 結構與其他資料形式.
函數應用(二)與自定函數.
陣列與結構.
指標、串列 (Linked List).
動畫演示 Node規範了一些基本的方法,像是增加節點、刪除節點、讓節點做一些事、取得第n個節點等等
北一女中 資訊選手培訓營 妳不可不了解的指標 Nan.
資料結構與C++程式設計進階 期末考 講師:林業峻 CSIE, NTU 7/ 15, 2010.
String類別 在C語言中提供兩種支援字串的方式 可以使用傳統以null結尾的字元陣列 使用string類別
Trees 授課者:驕芸.
Array(陣列) Anny
10303: How Many Trees? ★★☆☆☆ 題組:Contest Archive with Online Judge
第10章 二元搜尋樹 (Binary Search Tree)
Chapter 4 Multi-Threads (多執行緒).
台大資訊工程學系 資訊系統訓練班 第119期 吳晉賢
C 程式設計— 結構 台大資訊工程學系 資訊系統訓練班.
InputStreamReader Console Scanner
Presentation transcript:

第六章 二元搜尋樹 現在我們的注意焦點要轉到搜尋樹(search tree)了,要深度討論兩種標準的樹結構(tree structure),就是本章所要說明的二元搜尋樹(binary search tree)以及下一章所要討論的 AVL 平衡樹(AVL tree)。這兩種樹其資料都依序排列的,它們之間的差別只在於 AVL 是一種平衡樹,而二元搜尋樹卻不是。 前面章節在設計堆疊或佇列時,其資料結構都可選用陣列或連結串列,選用陣列對於搜尋作業其效率很高,但對於插入及移除卻沒有效率,選用連結串列對於搜尋作業其效率很差,但對於插入及移除卻有很高的效率,理想的結構是搜尋、插入、移除等作業其效率都要很好,答案就是使用二元搜尋樹或 AVL 平衡樹結構。

6.1 基本觀念 一棵二元搜尋樹是一棵二元樹,俱有下列的特性: 1. 所有左子樹其資料項均小於根節點。 2. 所有右子樹其資料項均大於或等於根節點。 3. 每一子樹本身也是一棵二元搜尋樹。

二元搜尋樹 一般而言,每一個節點所表示的資訊非僅是一個單純的資料,而是一組資料的結合,稱為一個記錄(record),在 C 語言裡屬於一種結構型態(struct)。當二元搜尋樹其節點的對象是一個記錄時,其順序為該記錄的鍵(key)值。如下圖所示。

五種二元搜尋樹舉例 下圖是五種二元搜尋樹舉例,(a)與(b)是一棵完整且平衡的二元搜尋樹,(d)是一棵接近完整且平衡的二元搜尋樹,(c)與(e)是既不完整也不平衡的二元搜尋樹。

6.2 二元搜尋樹資料結構 二元搜尋樹實作需要兩種不同的結構,一個頭結構以及一個資料結構。頭結構包含不屬資料的一些相關的資訊,例如資料節點計數、第一個資料節點指標、或是資料的比較方法,等等的資訊。 資料結構包含資料欄以及鏈欄,鏈欄的內含為下一個資料節點的指標。資料欄可以是資料本身,也可以是指到真正資料位置的指標,left 鏈欄指到下一個左子樹根節點,right 鏈欄指到下一個右子樹根節點,鏈欄其值若為 NULL 則表示不指到任何資料節點。

資料結構 資料結構宣告如下: struct bstNodeTag { void *dataptr; struct bstNodeTag *left; struct bstNodeTag *right; }; 本案例資料結構裡的資料欄宣告為不定型態的指標「void *dataptr」,不定型態的指標與任何型態的指標相匹配,使用時只須強迫轉型為指定的型態就可使用了。鏈欄 left 及 right 都是指標,它指到下一個子樹根節點。

頭結構 頭結構宣告如下: struct bstTag { int count; int (*compare) (void* arg1, void* arg2); struct bstNodeTag *root; }; 頭結構裡有三個成員,count 成員為節點計數。root 表二元搜尋樹根節點的指標。compare 是兩項資料比較的函式,資料項可以是整數、浮點數、字元、字串等等 C 語言所允許的資料型態,這個函式使用時必須由使用者提供。

6.3 建立頭結構初值 struct bstTag *bstCreate(int (*compare) (void *arg1, void *arg2)) { struct bstTag *tree; tree = malloc(sizeof(struct bstTag)); if (tree) tree->root = NULL; tree->count = 0; tree->compare = compare; } return tree;

6.4 加入 int bstInsert(struct bstTag *tree, void *dataptr) { struct bstNodeTag* newptr; newptr = malloc(sizeof(struct bstNodeTag)); if (!newptr) return 0; newptr->right = NULL; newptr->left = NULL; newptr->dataptr = dataptr; if (tree->count == 0) tree->root = newptr; else InsertBST(tree, tree->root, newptr); (tree->count)++; return 1; } 6.4 加入

6.5 取得 void *bstGet(struct bstTag *tree, void *keyptr) { if (tree->root) return GetBST(tree, keyptr, tree->root); else return NULL; }

6.6 移除 int bstRemove(struct bstTag *tree, void *delkey) { int success; struct bstNodeTag *newroot; newroot=RemoveBST(tree,tree->root,delkey,&success); if (success) tree->root = newroot; (tree->count)--; if (tree->count == 0) tree->root=NULL; } return success;

6.7 測試空白 int bstEmpty(struct bstTag *tree) { return (tree->count==0); }

6.8 測試滿載 int bstFull(struct bstTag *tree) { struct bstNodeTag * newptr; newptr = malloc(sizeof(*(tree->root))); if (newptr) free(newptr); return 0; } else return 1;

6.9 傳回資料項數 int bstCount(struct bstTag *tree) { return (tree->count); }

6.10 釋放記憶體 struct bstTag *bstRelease(struct bstTag *tree) { if (tree) ReleaseBST (tree->root); free (tree); return NULL; }

6.11 二元搜尋樹應用 將前面有關二元搜尋樹的資料結構以及函式存入 bst.h 表頭檔備用。表頭檔名 b 表 binary,s 表 search,t 表 tree,即二元搜尋樹的意思。

6.11.1 整數二元搜尋樹 執行下列 numbst.c 程式時,依序從 data[] 輸入下列的整數: 51 32 73 24 46 67 95 每輸入一個整數時就透過 bstInsert() 函式插入二元搜尋樹的適當位置,最後建立一個如下圖所示的 tree 二元搜尋樹。

建立一棵二元搜尋樹tree 要建立一棵二元搜尋樹必須提供一個比較函式,因為本題的資料為整數,因此將不定型態的指標「void *」num1 及 num2 強迫轉型為「int *」整數型態的指標 key1 及 key2,然後進行比較,將結果傳回。 int compare(void *num1, void *num2) { return *(int*)num1 - *(int*)num2; } 比較函式名為 compare,可當建立建立二元搜尋樹函式的引數,其程式碼如下: struct bstTag *tree; tree = bstCreate(compare);

依次從 data[] 輸入一個整數 data[i] for (i=0; i<sizeof(data)/sizeof(int); i++) { dataptr = (int *)malloc(sizeof(int)); *dataptr = data[i]; bstInsert(tree, dataptr); } 這一段程式依次從 data[] 輸入一個整數 data[i],取得整數大小的記憶體 dataptr 後將 data[i] 拷貝至此記憶體,然後透過 bstInsert() 函式將 dataptr 位址插入 tree 二元搜尋樹適當的位置。一直輸入到 data[] 沒有資料才結束輸入的作業,二元搜尋樹也才建立完成,如上圖 6.3 所示。

二元搜尋樹的遍訪 bstTraverse(tree, printNum); 這是對於 tree 二元搜尋樹的中序遍訪(inorder),printNum 是列印資料節點的函式,本例題只是將不定型態 void 指標轉成整數指標後列印其整數內容而已。 printf("\nPreorder :\n"); preorder(tree->root); 這是對於 tree 二元搜尋樹的前序遍訪(preorder)。 printf("\nPostorder :\n"); postorder(tree->root); 這是對於 tree 二元搜尋樹的後序遍訪(postorder)。

Inorder : 24 32 46 51 67 73 95 Preorder : 51 32 24 46 73 67 95 Postorder : 24 46 32 67 95 73 51

6.11.2 班上同學成績 上一個例題資料節點裡資料指標 dataptr 所指的是很單純的一個整數而已,本例題「班上同學成績」其 dataptr 所指的卻是一個學生資料結構,宣告如下: struct studentNodeTag { int id; char name[21]; float score; }; 該結構包含三個成員,id 為學生證號碼,宣告為整數,name 為姓名,宣告為 21 個字元的字串,字串以 '\0' 結束,score 欄為該生平均成績,宣告為浮點數。本結構包含三種不同資料型態的變數,整數、字串、浮點數。

同學資料從檔案student.txt輸入 每一個同學的資料均從檔案輸入,然後插入二元搜尋樹的適當位置。執行 student.c 程式時從本文檔 student.txt 輸入下列的資料: 105 Maya 86 103 Kay 82 109 Jay 79 107 Lina 75 115 Fay 80

學生成績二元搜尋樹

Void buildClass(struct bstTag *tree) { char buf[512]; struct studentNodeTag *sp; FILE *f = fopen("student.txt", "r"); while (fgets(buf,512,f)!=NULL) sp = malloc(sizeof(struct studentNodeTag)); sscanf(buf,"%d %s %f", &(sp->id), sp->name, &(sp->score)); bstInsert(tree, sp); } fclose(f); return; 執行本程式時,首先從檔案student.txt 輸入學生資料,建立一棵二元搜尋樹 tree,它是透過「buildClass(tree);」敘述完成的,其程式碼如左:

這是主程式的功能表,首先執行 getOption() 函式顯示一個功能選項表,並輸入一個字元 option,若該字元不是 'Q',就一直執行選項的工作,選 'A' 功能插入學生成績,'F' 功能找尋學生成績,'P' 功能印出二元搜尋樹 tree 所有節點資料,'R' 功能移除指定學生節點,這些功能的函式程式碼都很簡單,請直接參閱 student.c 程式,就不再逐一說明了。 while ( (option = getOption()) != 'Q') { switch (option) case 'A': insertStudent(tree); break; case 'F': findStudent(tree); break; case 'P': printTree(tree); break; case 'R': removeStudent(tree); break; }

檔案student.txt 105 Maya 86 103 Kay 82 109 Jay 79 107 Lina 75 115 Fay 80

【編譯執行】 ch05> gcc student.c -o student.exe <Enter> ch05> student.exe <Enter> ====== MENU ====== I Insert Student F Find Student P Print Class List R Remove Student Q Quit Enter option : p <Enter> List students in the class: id name score -------- -------------------- -------- 103 Kay 82.0 105 Maya 86.0 107 Lina 75.0 109 Jay 79.0 115 Fay 80.0

Find Student ====== MENU ====== I Insert Student F Find Student P Print Class List R Remove Student Q Quit Enter option : f <Enter> Enter student id : 109 <Enter> Student id: 109 name: Jay score: 79.0

Remove Student ====== MENU ====== I Insert Student F Find Student P Print Class List R Remove Student Q Quit Enter option : r <Enter> Enter student id : 109 <Enter>

Insert Student ====== MENU ====== I Insert Student F Find Student P Print Class List R Remove Student Q Quit Enter option : i <Enter> Enter id,name,score (EOF end) : 109 Peter 88.8 <Enter> Enter id,name,score (EOF end) : 108 Tom 77.7 <Enter> Enter id,name,score (EOF end) : ^Z <Enter>

Print Class List ====== MENU ====== I Insert Student F Find Student P Print Class List R Remove Student Q Quit Enter option : p <Enter> List students in the class: id name score -------- -------------------- -------- 103 Kay 82.0 105 Maya 86.0 107 Lina 75.0 108 Tom 77.7 109 Peter 88.8 115 Fay 80.0 Print Class List

Quit ====== MENU ====== I Insert Student F Find Student P Print Class List R Remove Student Q Quit Enter option : q <Enter>

6.12 字串二元搜尋樹 我們前面所建立的二元搜尋樹其資料節點裡的資料是一個不定型態的指標,其宣告如下: void *dataptr; 資料指標所指的可以是任何 C 語言所提供的資料型態,可以說以不變應萬變,不過對初學者來講完全透過指標的操作顯然有點複雜,不易了解,筆者另設計一套以字串資料為主的函式,操作較容易,說明如下。

6.12.1 資料結構 資料節點的結構宣告如下: #define STRLEN 80 struct strbstNodeTag { char data[STRLEN]; struct strbstNodeTag *right, *left; }; 宣告一個常數識別字 STRLEN 其值為 80,資料字串最長 80 個字元,此常數值可依需要隨時更改。結構名稱為 strbstNodeTag,包含三個成員,data 資料是 STRLEN 長度的字串,left 及 right 為指向此種節點的指標。

6.12.2 建立根節點指標 struct strbstNodeTag *strbstCreate() { struct strbstNodeTag *root; root = malloc(sizeof(struct strbstNodeTag)); root = NULL; return root; } 取得一塊記憶體 root,將其值設為 NULL 值,表示目前不指到任何節點,將 root 傳回。請注意它並不需指定比較函式 。

6.12.3 插入 void strbstInsert(struct strbstNodeTag **root, struct strbstNodeTag *itemptr) { if ((*root)==NULL) *root = itemptr; return; } if (strcmp(itemptr->data,(*root)->data)<0) strbstInsert(&(*root)->left, itemptr); else if (strcmp(itemptr->data,(*root)->data)>0) strbstInsert(&(*root)->right, itemptr);

6.12.4 取得 void *strbstGet(struct strbstNodeTag *root, void *keyptr) { if (root) if (strcmp(keyptr, root->data) < 0) return strbstGet(root->left, keyptr); else if (strcmp(keyptr, root->data) > 0) return strbstGet(root->right, keyptr); else return root->data; } return NULL; 6.12.4 取得

6.12.5 移除 int strbstRemove(struct strbstNodeTag *root, void *delkey) { int success; struct strbstNodeTag *newroot; newroot = RemoveStrbst(root, delkey, &success); if (success) root = newroot; else root = NULL; return success; }

6.12.6 測試空白 int strbstEmpty(struct strbstNodeTag *root) { return (root==NULL); }

6.12.7 測試滿載 int strbstFull(struct strbstNodeTag *root) { struct strbstNodeTag *newptr; newptr = malloc(sizeof(*(root))); if (newptr) free(newptr); return 0; } else return 1;

6.12.8 傳回資料項數 int strbstCount(struct strbstNodeTag *root) { int count=0; if (root==NULL) return 0; CountStrbst(root, &count); return count; }

6.12.9 釋放記憶體 void *strbstRelease(struct strbstNodeTag *root) { if (root) strbstRelease(root->left); free(root); strbstRelease(root->right); } return NULL;

6.12.10 測試程式 以上所說明有關字串二元搜尋樹的資料結構宣告,以及相關的函式存入表頭檔 strbst.h 備用。str 表字串 string,b 表二元 binary,s 表搜尋 search,t 表樹結構 tree。 在測試程式 strbst.c 裡頭所準備的輸入資料 data[] 如下: "JMP" "COMP" "MUL" "DIV" "ADD" "SUB" "LOOP" "LEA" "MOV" 一共九個字串,依次透過 strbstInsert() 函式建立一個 root 字串二元搜尋樹,如下圖所示。

建立字串二 元搜尋樹

遍訪及移除 然後分別測試前序遍訪、後序遍訪、中序遍訪等函式是否正確。然後測試移除 "MUL" 字串的作業,移除節點的作業較複雜,說明如下: 1. 在二元搜尋樹裡找到移除鍵 "MUL" 節點 p。 2. 找到 p 節點之左子樹中最大者 "MOV" 之節點 x。 3. 將 p 節點資料("MUL")與 x 節點資料("MOV")互換。 4. 移除 x 節點("MUL")。

移除"MUL"節點之前

移除"MUL"節點之後

重新插入"MUL"節點之後