第七章 AVL搜尋樹 二元搜尋樹簡單易懂,不過有一個問題:它並非平衡樹。本章將介紹平衡的 AVL 搜尋樹,討論它的資料結構、函式,並設計程式使用它。

Slides:



Advertisements
Similar presentations
1 第八章 深入探討樹狀結構. 2 目次 8.1 m-way 搜尋樹 8.2 B-tree 8.3 動動腦時間 8.4 練習題解答.
Advertisements

第一單元 建立java 程式.
第7章 樹與二元樹 (Trees and Binary Trees)
資料結構 老師:李崇明 助教:楊斯竣.
Tree (2): heap, deap, 2-3 tree, 2-3-4tree
資料結構(計財系).
第6章 二叉树和树 前面的章节主要讨论的是线性结构,二叉树和树属于非线性的结构。遍历非线性结构比线性结构要麻烦。
第八章 查找.
陳維魁 博士 儒林圖書公司 第九章 資料抽象化 陳維魁 博士 儒林圖書公司.
資料結構 第3章 鏈結串列.
資料結構設計與C++程式應用 Fundamentals of Data Structures and Their Applications Using C++ 第3章 佇列 資料結構設計與C++程式應用.
第7章 樹與二元樹 (Trees and Binary Trees)
101北一女中 資訊選手培訓營 妳不可不了解的指標 Nan.
第七章 搜索结构 静态搜索结构 二叉搜索树 AVL树.
第12章 樹狀搜尋結構 (Search Trees)
資料結構 第6章 樹狀結構.
類別(class) 類別class與物件object.
SQL Stored Procedure SQL 預存程序.
(Circular Linked Lists)
本章重点难点 重点:顺序查找、二分查找、二叉排序树查找以及散列表查找的基本思想和算法实现。
資料結構–樹(Tree) 綠園.
鄧姚文 資料結構 第六章:樹(Tree) 鄧姚文
Chapter 9 高度平衡二元搜尋樹 AVL Binary Search Tree
Java 程式設計 講師:FrankLin.
JAVA 程式設計與資料結構 第四章 陣列、字串與數學物件.
Chap3 Linked List 鏈結串列.
Chapter 11 B-tree 11.1 m-way 搜尋樹 11.2 B-tree.
数据结构 第八章 查找.
Ch 3 Dynamic Programming (動態規劃).
程式設計實習課(四) ----C 函數運用----
第一單元 建立java 程式.
Chap4 Tree.
第二次電腦實習課 說明者:吳東陽 2003/10/07.
資料結構 優點 缺點 1 陣列 (Array) 沒有額外變量 (例head, next,...) 運作/操作較簡單 更新資料時,若要保持順序,需要移動較大量資料 靜態結構Static (宣告時已決定了陣列元素多少,不能在程式執行期間增減元素) 2 隊列Queue (FIFO) 容易更新 加入enqueue:
資料結構使用Java 樹(Tree).
第 19 章 XML記憶體執行模式.
Reference to FUNDAMENTALS OF DATA STRUCTURE IN C++
輸入&輸出 函數 P20~P21.
Introduction to C Programming
第7章 樹與二元樹(Trees and Binary Trees)
挑戰C++程式語言 ──第8章 進一步談字元與字串
資料結構與C++程式設計進階 樹狀結構(Tree) 講師:林業峻 CSIE, NTU 11/ 12, 2009.
Class & Object 靜宜大學資工系 蔡奇偉副教授 ©2011.
9.1 何謂高度平衡二元搜尋樹 9.2 AVL-tree的加入 9.3 AVL-tree的刪除
C qsort.
第八章 堆 積 堆積(heap)是樹結構的第三種型態。堆積是一棵二元樹,其左右子樹節點的值均較其父母節點的值小。堆積的根節點值保證是該樹最大值。這中堆績稱為最大堆績。堆積的子樹可擺在左邊當左子樹,也可擺在右邊當右子樹,因此左右子樹俱有相同的性質。 堆積還有一個有趣的特性,就是以陣列實作較以連結串列實作佳。當以陣列實作堆積時,已知父母節點的註標(subscript)就可算出其左右子樹節點的註標,反之亦然。
講師:郭育倫 第3章 基本資料結構 講師:郭育倫
挑戰C++程式語言 ──第7章 輸入與輸出.
陣列與結構.
第六章 二元搜尋樹 現在我們的注意焦點要轉到搜尋樹(search tree)了,要深度討論兩種標準的樹結構(tree structure),就是本章所要說明的二元搜尋樹(binary search tree)以及下一章所要討論的 AVL 平衡樹(AVL tree)。這兩種樹其資料都依序排列的,它們之間的差別只在於.
Chapter 15 檔案存取 LabVIEW中的檔案存取函數也可將程式中的資料儲存成Excel或Word檔。只要將欲存取的檔案路徑位址透過LabVIEW中的路徑元件告訴檔案存取函數後,LabVIEW便可將資料存成Excel或Word檔;當然也可以將Excel或Word檔的資料讀入LabVIEW的程式中。
動畫演示 Node規範了一些基本的方法,像是增加節點、刪除節點、讓節點做一些事、取得第n個節點等等
6.1 樹狀結構的一些專有名詞 6.2 二元樹 6.3 二元樹的表示方法 6.4 二元樹的追蹤 6.5 引線二元樹 6.6 其它議題
北一女中 資訊選手培訓營 妳不可不了解的指標 Nan.
1757: Secret Chamber at Mount Rushmore
資料表示方法 資料儲存單位.
程式設計--Quick Sort 通訊一甲 B 楊穎穆.
資料結構與C++程式設計進階 期末考 講師:林業峻 CSIE, NTU 7/ 15, 2010.
What is “this”? 在物件導向程式設計中,類別的定義就是在說明如果創建了“這個物件”的話,它會具有那些屬性與功能,以及這些功能是如何實現的。 而所謂的“這個物件”就以 this 來表示。 當我們在JavaScript與jQuery中寫 script 程式(函式)時,“誰”呼叫這個函式,這個“誰”就是該函式中所謂的.
All Sources Shortest Path The Floyd-Warshall Algorithm
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
String類別 在C語言中提供兩種支援字串的方式 可以使用傳統以null結尾的字元陣列 使用string類別
Trees 授課者:驕芸.
Array(陣列) Anny
10303: How Many Trees? ★★☆☆☆ 題組:Contest Archive with Online Judge
第10章 二元搜尋樹 (Binary Search Tree)
Chapter 4 Multi-Threads (多執行緒).
本节内容 1. 平衡二叉树的定义 2.平衡化旋转 3.平衡二叉排序树的插入操作 昆山爱达人信息技术有限公司
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
InputStreamReader Console Scanner
Presentation transcript:

第七章 AVL搜尋樹 二元搜尋樹簡單易懂,不過有一個問題:它並非平衡樹。本章將介紹平衡的 AVL 搜尋樹,討論它的資料結構、函式,並設計程式使用它。

7.1 基本觀念 AVL 樹是一種搜尋樹,其左右子樹最大的高度差為一,因此稱為二元平衡樹。為了凸顯平衡與不平衡的差異,請看下列的二元樹。

AVL 樹的定義 | HL - HR | <= 1 AVL 樹的定義如下: 一顆 AVL 樹是一顆二元樹,或為空白,或含兩 顆 AVL 子樹 TL 及 TR,其高度最多相差一。 | HL - HR | <= 1 HL 為左子樹高度,HR 為右子樹高度。 因為 AVL 樹以高度來衡量平衡度,故也稱為高度平衡樹。因為任何一個節點的平衡因子(balance factor)必須為 +1、0、-1,我們用一個常數識別字來表示,LH 表示 Left High 左子樹較右子樹高一層(+1),LE 表示 Left Equal 左右子樹高度相同(0),RH 表示 Right High 右子樹較左子樹高一層,亦即左子樹較右子樹低一層(-1)。

7.2 平衡樹 每當我們插入一個節點進入一顆樹,或從一顆樹移除一個節點,都會破壞平衡,必須重新平衡它。每當偵測到一顆樹已經失去平衡的時候,就 必須再平衡一次。AVL 樹的平衡是透過節點向左旋轉(rotate)或向右旋轉達成的。本小節將說明基本的平衡演算法。

左高再插入左方 如右圖,19 的左子樹已較高,現在再插入 5 於左子樹,則 19 的左子樹(13)變成高於右子樹(21)兩個層次,變成非 AVL 樹,因此必須再平衡一下。

將19節點往右旋轉降下一個層次 上圖顯示一個較複雜的問題,子樹 13 是平衡的,但整棵樹 19 則並不平衡,因此必須將 19 向右旋轉,讓它成為 13 節點的右子樹,這又產生另一個問題,目前當 13 節點右子樹的 15 節點如何安排?節點 19 向右旋轉時已失掉它的左子樹了,節點 15 就當 19 節點的左子樹好了,這也合乎二元搜尋樹的規定呢。

右高再插入右方 如上圖,15 的右子樹已較高,現在再插入 45 於右子樹,則 15 的右子樹變成高於左子樹兩個層次,變成非 AVL 樹,因此必須再平衡一下。

將15節點往左旋轉降下一個層次 上圖顯示一個較複雜的問題,子樹 13 是平衡的,但整棵樹 15 則並不平衡,因此必須將 15 向左旋轉,讓它成為 21 節點的左子樹,這又產生另一個問題,目前當 21 節點左子樹的 19 節點如何安排?節點 15 向左旋轉時已失掉它的右子樹了,節點 19 就當 15 節點的右子樹好了,這也合乎二元搜尋樹的規定呢。

7.2.3 左方右 前面兩種情形,左方左及右方右,都是只需一次旋轉就可完成平衡,但左方右或右方左均需要兩次的旋轉才可完成平衡,下圖說明左方右的兩次旋轉例子。

7.2.4 右方左 下圖說明右方左的兩次旋轉例子。

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

AVL樹資料結構 資料結構宣告如下: #define LH +1 /*Left High*/ #define EH 0 /*Even High*/ #define RH -1 /*Right High*/ struct avlNodeTag { void *dataptr; int balance; struct avlNodeTag *left, *right; }; 本案例資料結構裡的資料欄宣告為不定型態的指標「void *dataptr」,不定型態的指標與任何型態的指標相匹配,使用時只須強迫轉型為指定的型態就可使用了。平衡因子 balance 為整數。left 及 right 鏈欄都是指標,它指到下一個子樹根節點。

AVL樹頭結構 頭結構宣告如下: struct avlTag { int count; int (*compare) (void* arg1, void* arg2); struct avlNodeTag *root; }; 頭結構裡有三個成員,count 成員為節點計數。root 表 AVL 二元搜尋樹根節點的指標。compare 是兩項資料比較的函式名稱。

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

7.3.3 加入 int avlInsert(struct avlTag *tree, void *dataptr) { struct avlNodeTag *p; int taller; p = malloc(sizeof(struct avlNodeTag)); if (p==NULL) return 0; p->balance = EH; p->right = NULL; p->left = NULL; p->dataptr = dataptr; tree->root=InsertAVL(tree,tree->root,p,&taller); (tree->count)++; return 1; } 7.3.3 加入

7.3.4 取得 void * avlGet(struct avlTag *tree, void *keyptr) { if (tree->root) return GetAVL(tree, keyptr, tree->root); else return NULL; }

7.3.5 移除 int avlRemove(struct avlTag *tree, void * delkey) { int shorter; int success; struct avlNodeTag *p; p = RemoveAVL(tree, tree->root, delkey, &shorter, &success); if (success) tree->root = p; (tree->count)--; return 1; } else return 0; 7.3.5 移除

7.3.6 測試空白 int avlEmpty(struct avlTag *tree) { return (tree->count == 0); }

7.3.7 測試滿載 int avlFull(struct avlTag *tree) { struct avlNodeTag *p; p = malloc(sizeof(*(tree->root))); if (p!=NULL) free(p); return 0; } else return 1;

7.3.8 傳回資料項數 int avlCount(struct avlTag *tree) { return (tree->count); }

7.3.9 釋放記憶體 struct avlTag *avlRelease(struct avlTag *tree) { if (tree!=NULL) ReleaseAVL(tree->root); free(tree); return NULL; }

7.4 AVL 二元搜尋樹應用 將前面有關 AVL 二元搜尋樹的資料結構以及函式存入 avl.h 表頭檔備用。

7.4.1 數字所建平衡樹 struct avlTag *tree; tree = avlCreate(compare); 程式 numavl.c 從整數陣列 k 取得一系列的整數如下: 53 45 24 21 19 15 13 9 若以此建立一般的二元樹的話,它是斜向左邊的不平衡樹,其高度差為八。以此資料建立一棵 AVL 樹,其高度為四,高度差 HL-HR=1。 struct avlTag *tree; tree = avlCreate(compare); 首先建立一棵空白的 AVL 樹 tree。

插入 AVL 樹 tree 裡的適當位置 for (i=0; i<sizeof(k)/sizeof(int); i++) { dataptr = (int *)malloc(sizeof(int)); *dataptr = k[i]; avlInsert(tree, dataptr); } 取得一塊記憶體 dataptr,將 k[i] 存入 dataptr 記憶體,然後透過 avlInsert() 函式將 dataptr 資料插入 AVL 樹 tree 裡的適當位置。

中序遍訪每一個節點 avlTraverse(tree, printNum); dumpTree(tree->root); 刻意以前序遍訪每一個節點,將該節點的每一個資料欄位值列印出來核對 AVL 樹是否正確,跟據這些資料可畫出整棵 AVL 樹,如下圖 7.8 所示。

The AVL binary search tree contains : 9 13 15 19 21 24 45 53 input data : 53 45 24 21 19 15 13 9 The AVL binary search tree contains : 9 13 15 19 21 24 45 53 從執行結果可以看出,我們的輸入資料 53 至 9 是已經由大到小排成順序的,以一般的二元搜尋樹是不平衡的,從根節點往下,每一層次都沒有右子樹,但建成 AVL 樹後變成平衡樹如上圖所示。

當依序插入 53、45、24 後,原本的樹應該如下圖(a)所示,左子樹原已較高,今插入 24 節點後左子樹更高了,因此必須平衡,將根節點的 53 向右旋轉,變成 45 節點的右子樹,如下圖的(b)所示。

插入 21、19 後,原本的樹應該如下圖(a)所示,左子樹原已較高,今插入 19 節點後左子樹更高了,因此必須平衡,節點 24 向右旋轉變成 21 節點的右子樹,如下圖的(b)所示。

插入 15 後,原本的樹應該如下圖(a)所示,左子樹原已較高,今插入 15 節點後左子樹更高了,因此必須平衡,節點 45 向右旋轉變成節點 21 的右子樹,如下圖的(b)所示。

插入 13 後,原本的樹應該如下圖(a)所示,左子樹原已較高,今插入 13 節點後左子樹更高了,因此必須平衡,節點 19 向右旋轉變成節點 15 的右子樹,如下圖的(b)所示。

7.4.2 字組統計 上一個例子 numavl.c 所使用的資料只是單純的整數,現在要舉一個屬於結構的資料。 程式 words.c 從一個本文檔案 zoo.txt 讀取一篇本文,它是台北木柵動物園的園區介紹,其資料如下: Formasan Animal Area, Children's Zoo Area, Asian Tropical rainforests Area, Desert Animal Area, Australian Animal Area, Africa Animal Area, Temperate Zone Animal Area, Bird World Area. 將此本文讀入,統計每一個字組出現的次數。

字組所使用的資料結構 每一個字組所使用的資料結構宣告如下: #define STRLEN 21 struct wordNodeTag { char word[STRLEN]; int count; }; 識別字常數 STRLEN 定義為 21,表示字組 word 最長為二十個字元。count 表該字組出現的次數。

比較函式 int compareWords(void *arg1, void *arg2) { struct wordNodeTag *wp1=arg1; struct wordNodeTag *wp2=arg2; return (strcmp(wp1->word, wp2->word)); } 比較函式命名為 compareWords,對於字組結構裡的字組字串相比較,傳回其比較的結果,前者較小則傳回負整數,相等則傳回零值,較大則傳回正整數。

主程式的作業 struct avlTag *tree; tree = avlCreate(compareWords); buildTree(tree); printTree(tree); printf("\ndump AVL tree:"); dumpTree(tree->root); 這是主程式的作業,首先建立一棵空白的 AVL 二元樹 tree 使用比較 compareWords 函式,然後透過 buildTree() 函式從 zoo.txt 本文檔讀取本文建立一棵 AVL 樹 tree。使用 printTree() 函式以中序遍訪列印出該樹的所有節點。最後刻意以前序遍訪傾印每一個節點的欄位值以利核對。

words.c執行後所建立的AVL平衡樹

7.4.3 字串平衡樹 從字串陣列 k[ ] 取得每一個字串元素,建立一個 AVL 平衡樹,資料如下: #define STRLEN 16 char k[ ][STRLEN]={"Zone", "Bird", "Asian", "Animal", "Africa", "Australian", "Formasan"};

"Zone"向右旋轉成"Bird"右子樹 依序插入 "Zone"、"Bird"、"Asian" 後,原本的樹應該如下圖(a)所示,左子樹原已較高,今插入 "Asian" 節點後左子樹更高了,因此必須平衡,將根節點的 "Zone" 向右旋轉,變成 "Bird" 節點的右子樹,如下圖的(b)所示。

"Asian"向右旋轉成"Animal"右子樹 插入 "Animal"、"Africa" 後,原本的樹應該如下圖(a)所示,左子樹原已較高,今插入 "Africa" 節點後左子樹更高了,因此必須平衡,將節點 "Asian" 向右旋轉,變成 "Animal" 節點的右子樹,如下圖的(b)所示。

"Animal"左轉"Bird"右轉達成平衡 插入 "Austrlian" 後,原本的樹應該如下圖(a)所示,左子樹原已較高,今插入 "Australian" 節點後變成右子樹較高了,因此必須平衡,將節點 "Animal" 向左旋轉,變成 "Asian" 節點的左子樹,如下圖的(b)所示。這時左子樹較高,仍未平衡,將節點 "Bird" 向右旋轉,當節點 "Asian" 的右子樹,才完成平衡,如下圖(c)所示。

字串平衡樹 最後將 "Formasan" 插入 "Zone" 左邊,完成平衡樹,如下圖所示。

若輸入反序,從 9 至 53,則所建立的 AVL 樹會不一樣的,資料相同,但順序不同,所建立的樹當然不一樣,如下圖所示。