第八章 堆 積 堆積(heap)是樹結構的第三種型態。堆積是一棵二元樹,其左右子樹節點的值均較其父母節點的值小。堆積的根節點值保證是該樹最大值。這中堆績稱為最大堆績。堆積的子樹可擺在左邊當左子樹,也可擺在右邊當右子樹,因此左右子樹俱有相同的性質。 堆積還有一個有趣的特性,就是以陣列實作較以連結串列實作佳。當以陣列實作堆積時,已知父母節點的註標(subscript)就可算出其左右子樹節點的註標,反之亦然。

Slides:



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

第一單元 建立java 程式.
Introduction to C Programming
計算機程式語言實習課.
資料結構 老師:李崇明 助教:楊斯竣.
Tree (2): heap, deap, 2-3 tree, 2-3-4tree
陳維魁 博士 儒林圖書公司 第九章 資料抽象化 陳維魁 博士 儒林圖書公司.
Project 2 JMVC code tracing
Chapter 5 遞迴 資料結構導論 - C語言實作.
資料結構 第3章 鏈結串列.
結構(struct).
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置 4-2 鏈結串列的基礎 4-3 單向鏈結串列 4-4 環狀鏈結串列
LINQ 建國科技大學 資管系 饒瑞佶.
簡易C++除錯技巧 長庚大學機械系
資料結構設計與C++程式應用 Fundamentals of Data Structures and Their Applications Using C++ 第3章 佇列 資料結構設計與C++程式應用.
第11章 資料結構 – 使用C語言的模組 11-1 資料結構的基礎 11-2 C語言的模組化程式設計 11-3 基本鏈結串列
第7章 樹與二元樹 (Trees and Binary Trees)
JDK 安裝教學 (for Win7) Soochow University
第七章 AVL搜尋樹 二元搜尋樹簡單易懂,不過有一個問題:它並非平衡樹。本章將介紹平衡的 AVL 搜尋樹,討論它的資料結構、函式,並設計程式使用它。
101北一女中 資訊選手培訓營 妳不可不了解的指標 Nan.
4B冊 認識公倍數和最小公倍數 公倍數和最小公倍數的關係.
資料結構與C++程式設計進階 鏈結串列 講師:林業峻 CSIE, NTU 6/ 10, 2010.
類別(class) 類別class與物件object.
(Circular Linked Lists)
4.1 單向鏈結串列 4.2 堆疊的加入與刪除 4.3 佇列的加入與刪除 4.4 其他型式的佇列
鄧姚文 資料結構 第六章:樹(Tree) 鄧姚文
Chapter 9 高度平衡二元搜尋樹 AVL Binary Search Tree
Java 程式設計 講師:FrankLin.
FTP檔案上傳下載 實務與運用.
Chap3 Linked List 鏈結串列.
Chapter 11 B-tree 11.1 m-way 搜尋樹 11.2 B-tree.
Ch 3 Dynamic Programming (動態規劃).
第一單元 建立java 程式.
Chap4 Tree.
資料結構 7-1 陣列 7-2 鏈結串列 7-3 堆疊和佇列 7-4 樹狀結構.
第三章 資料型態與輸出控制 本章學習目標 認識Matlab的基本資料型態 練習資料型態的轉換 學習如何控制Matlab的輸出格式
資料結構 優點 缺點 1 陣列 (Array) 沒有額外變量 (例head, next,...) 運作/操作較簡單 更新資料時,若要保持順序,需要移動較大量資料 靜態結構Static (宣告時已決定了陣列元素多少,不能在程式執行期間增減元素) 2 隊列Queue (FIFO) 容易更新 加入enqueue:
第 19 章 XML記憶體執行模式.
Reference to FUNDAMENTALS OF DATA STRUCTURE IN C++
挑戰C++程式語言 ──第8章 進一步談字元與字串
資料結構與C++程式設計進階 樹狀結構(Tree) 講師:林業峻 CSIE, NTU 11/ 12, 2009.
第 八 章 高等樹 課程名稱:資料結構 授課老師:________ 2019/4/28.
引用檔案.
C qsort.
講師:郭育倫 第3章 基本資料結構 講師:郭育倫
資料結構使用Java 第6章 鏈結串列(Linked List).
流程控制:Switch-Case 94學年度第一學期‧資訊教育 東海大學物理系.
陣列與結構.
第六章 二元搜尋樹 現在我們的注意焦點要轉到搜尋樹(search tree)了,要深度討論兩種標準的樹結構(tree structure),就是本章所要說明的二元搜尋樹(binary search tree)以及下一章所要討論的 AVL 平衡樹(AVL tree)。這兩種樹其資料都依序排列的,它們之間的差別只在於.
Chapter 15 檔案存取 LabVIEW中的檔案存取函數也可將程式中的資料儲存成Excel或Word檔。只要將欲存取的檔案路徑位址透過LabVIEW中的路徑元件告訴檔案存取函數後,LabVIEW便可將資料存成Excel或Word檔;當然也可以將Excel或Word檔的資料讀入LabVIEW的程式中。
動畫演示 Node規範了一些基本的方法,像是增加節點、刪除節點、讓節點做一些事、取得第n個節點等等
北一女中 資訊選手培訓營 妳不可不了解的指標 Nan.
第7章 資料結構 7-1 陣列 7-2 鏈結串列 7-3 堆疊和佇列 7-4 樹狀結構.
資料表示方法 資料儲存單位.
程式設計--Quick Sort 通訊一甲 B 楊穎穆.
資料結構與C++程式設計進階 期末考 講師:林業峻 CSIE, NTU 7/ 15, 2010.
第四章 陣列、指標與參考 4-1 物件陣列 4-2 使用物件指標 4-3 this指標 4-4 new 與 delete
堆積(Heap Tree) 授課老師:蕭志明.
第四組 停車場搜尋系統 第四組 溫允中 陳欣暉 蕭積遠 李雅俐.
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
String類別 在C語言中提供兩種支援字串的方式 可以使用傳統以null結尾的字元陣列 使用string類別
Trees 授課者:驕芸.
Array(陣列) Anny
10303: How Many Trees? ★★☆☆☆ 題組:Contest Archive with Online Judge
Chapter 4 Multi-Threads (多執行緒).
C語言程式設計 老師:謝孟諺 助教:楊斯竣.
Unix指令4-文字編輯與程式撰寫.
Develop and Build Drives by Visual C++ IDE
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
InputStreamReader Console Scanner
Presentation transcript:

第八章 堆 積 堆積(heap)是樹結構的第三種型態。堆積是一棵二元樹,其左右子樹節點的值均較其父母節點的值小。堆積的根節點值保證是該樹最大值。這中堆績稱為最大堆績。堆積的子樹可擺在左邊當左子樹,也可擺在右邊當右子樹,因此左右子樹俱有相同的性質。 堆積還有一個有趣的特性,就是以陣列實作較以連結串列實作佳。當以陣列實作堆積時,已知父母節點的註標(subscript)就可算出其左右子樹節點的註標,反之亦然。 若父母節點的註標 i 從 0 開始計數時,則左子樹節點的註標為 2*i+1,右子樹節點的註標為 2*i+2。 若父母節點的註標 i 從 1 開始計數時,則左子樹節點的註標為 2*i,右子樹節點的註標為 2*i+1。 已知子女節點的註標也可算出其父母節點的註標,因此處理的效率相當高。

8.1 基本觀念 一棵堆積是一棵二元樹,俱備下列的特性: 1. 這棵樹是一棵完整或接近完整的二元樹。 2. 每一節點的鍵值大於或等於其子樹節點鍵值。 一棵完整的二元樹是每一層次(level)都佔滿,一棵接近完整的二元樹指其葉節點相差在一個層次以內。堆積如下圖所示。

三種堆積舉例 在上圖的兄弟節點裡,左節點有大於右節點者(13>9),左節點有小於右節點者(7<11,19<24),這對於前面章節說明過的二元搜尋樹是不允許的。注意上圖(c)是一棵接近完整的二元樹,第二層次是從左而右填滿的,這也是堆積的要求。

堆積有兩個基本的操作 對於堆積有兩個基本的操作:插入一個節點以及移除一個節點。雖然堆積是一棵樹,但對它執行遍訪、搜尋、列印的操作並無意義。要實作堆積的插入及移除節點操作,我們需要兩個演算法,再堆上(reheap up)以及再堆下(reheap down)。

8.1.1 再堆上作業 假想我們有一個 N 個元素的接近完整二元樹,其前面的 N-1 個元素均已滿足堆積的要求,但最後一個元素 N 卻不滿足堆積,換句話說只要第 N 個元素滿足堆積的要求,整個結構就是一個堆積了。再堆上作業將第 N 個元素往上堆,定位於正確的位置,使整個結構成為一個堆積。

26往上移動 上圖新元素 26 插入原來已成堆積的樹。最後的元素規定要擺在最右邊的。26 比它的父母節點 13 還大,違反堆積的規定,必須互換,因此就往上移動,如下圖所示。

26再往上移動 26 比它的父母節點 43 還小,就不必再移動,已經定到正確的位置了。這種再堆上使成堆積的作業稱為再堆上操作。

8.1.2 再堆下作業 再堆下作業事實上是再堆上的相反操作。舉例如下圖。

8.2 堆積實作 雖然堆積可以透過動態樹結構建立起來,但最常見的還是以陣列實作為宜。一個節點與其子女節點的關係可以計算出來,如下所述: 1. 若節點註標為 i(從0算起),則其子女節點的註標計算 如下: a). 左子女節點註標為 2*i+1 b). 右子女節點註標為 2*i+2 2. 若節點註標為 i(從0算起),則其父母節點的註標計算 如下:取 (i-1)/2 之整數部份。 3. 左子女節點註標為 j,右子女節點註標為 j+1。 右子女節點註標為 k,左子女節點註標為 k-1。 4. 設完整堆積元素數為 n,第一個葉節點為 (n/2) 之整數部份。最後的枝節點註標為 (n/2)-1 之整數部份。

堆積以樹及陣列表示 1. 33 的註標為 2(從0算起),左子女節點 24 註標 為 2*2+1=5,右子女節點 20 註標為 2*2+2=6。 2. 9 的註標為 4(從0算起),其父母節點 57 的註標 為 (4-1)/2 整數部份為 1。 3. 總共有 7 個元素(n=7),第一個葉節點為 7/2 整數部份為 3。 4. 最後的枝節點 33 註標為 (n/2)-1=(7/2)-1=2。

8.3 堆積結構 struct heapTag { void **data; int last; int size; int (*compare) (void *arg1, void *arg2); int maxsize; };

data指標變數的含意 這種結構就命名為 heapTag 結構。請注意 data 是 void 不定型態指標的指標,也就是說傳給此結構的資料本身是一個位址,此位址存於指標變數 *data 裡頭,此 *data 欄的位址卻存於 **data 指標變數裡頭,如下圖所示。

8.4 建立堆積結構 struct heapTag *heapCreate(int maxsize, int (*compare) (void *arg1, void *arg2)) { struct heapTag *heap; heap = malloc(sizeof(struct heapTag)); if (heap == NULL) return NULL; heap->last = -1; heap->size = 0; heap->compare = compare; heap->maxsize = (int)pow(2,ceil(log2(maxsize)))-1; heap->data = (void*) calloc( heap->maxsize, sizeof(void *)); return heap; } 8.4 建立堆積結構

建立堆積結構 需要提供兩個引數,第一個是最大容量之元素個數 maxsize,第二個是比較的函式名稱 compare。首先取得一塊 heapTag 結構的 heap 記憶體,設定 heap->last 為 -1,heap->size 為 0,heap->mazsize 最大容量為 2 的 log10(maxsize)/log10(2) 次方值再減一,例如 mazsize 引數值為 16,那麼 heap->maxsize 的值為 2 的 4 次方再減一,為 7。

建立堆積結構

8.5 插入堆積 int heapInsert(struct heapTag *heap, void *dataptr) { if (heap->size == 0) heap->size = 1; heap->last = 0; heap->data[heap->last] = dataptr; return 1; } if (heap->last == heap->maxsize - 1) return 0; ++(heap->last); ++(heap->size); ReheapUp(heap, heap->last); 8.5 插入堆積

void ReheapUp(struct heapTag *heap, int child) { int parent; void **data, **temp; if (child != 0) data = heap->data; parent = (child - 1)/ 2; if (heap->compare(data[child],data[parent])>0) temp=data[parent]; data[parent]=data[child]; data[child]=temp; ReheapUp (heap, parent); } return;

8.6 從堆積移除節點 int heapRemove(struct heapTag *heap, void **dataptr) { if (heap->size == 0) return 0; *dataptr = heap->data[0]; heap->data[0] = heap->data[heap->last]; (heap->last)--; (heap->size)--; ReheapDown(heap, 0); return 1; }

從堆積移除節點 從堆積 heap 裡移除資料 *dataptr 所指的節點,該指標為資料陣列第零個元素的位址,即 heap->data[0],它是堆積裡最大值者,然後將最後的元素移到陣列第零個元素的位址,進行 ReheapDown() 再堆下作業,使滿足堆積的要求。ReheapDown() 再堆下函式屬於遞迴函式,一層一層往下堆積,直到最後一個元素那一層時才停止。最後將 last 及 size 成員值均減一。傳回 1 值。若 size 成員值已達 0 值則傳回 0 值。

8.7 測試程式 上面說明的堆積結構宣告以及相關的函式存入 heap.h 表頭檔備用。 下列的程式 heap.c 用於測試 heap.h 表頭檔的函式是否正確無誤。首先建立一個 heap 堆積,最大元素個數為 16,資料比較函式名稱為 compareInt。 struct heapTag *heap; heap = heapCreate(16,compareInt); 執行時從整數陣列 k[ ] 依序輸入下列的數字。 9 24 45 13 21 53 15 19

依序插入9,24,45,13,21時之堆積

依序插入53,15時之堆積

依序插入19時之堆積及陣列 根節點為第 0 個元素,其值為 53 最大,它的左子樹在第 1 個位置,其值為 21,它的右子樹在第 2 個位置,其值為 45。 節點 21 在第 1 個位置,左子樹在第 2*1+1=3 個位置,其值為 19,右子樹在第 2*1+2=4 個位置,其值為 13。 節點 45 在第 2 個位置,左子樹在第 2*2+1=5 個位置,其值為 24,右子樹在第 2*2+2=6 個位置,其值為 15。 節點 19 在第 3 個位置,左子樹在第 2*3+1=7 個位置,其值為 9,沒有右子樹。

8.8 優先佇列 許多出國旅遊的人在機場登機時,常看到機組員最先登機,然後持有貴賓卡的人登機,然後孕婦或帶嬰兒的人登機,最後才是一般乘客登機,很顯然因為身份的不同,處理的方式也不同,某些人要優先處理。 就以這四類的人員說明。設機組員的優先碼為 4,貴賓優先碼為 3,孕婦優先碼為 2,一般乘客優先碼為 1。登機時機組員佇列在最前面,然後為貴賓佇列,其後為孕婦佇列,最後才是一般乘客佇列。

優先佇列 我們使用電腦來模擬,若每一個人有一個識別碼,一般是護照號碼或身份證字號,還有一個優先碼,這四類的人有先來後到的,若輸入檔案的內容如下: 101 1 103 3 104 4 204 4 202 2 203 3 201 1 301 1 304 4 302 2 303 3 第一個欄位為識別碼,為了簡單起見識別碼採用三位數字,第二欄為優先碼,一位數字 1~4,優先碼 4 最優先。

優先佇列 現在我們要從中選出各類人員,各自獨立一個佇列如下: 機組員 貴賓 孕婦 一般乘客 機組員 貴賓 孕婦 一般乘客 ------ ----- ----- -------- 104 4 103 3 202 2 101 1 204 4 203 3 302 2 201 1 304 4 303 3 301 1

優先佇列 第一個登機的給順號 999,第二個登機的給順號 998,逐次遞減,此順號配合優先碼,構成一個序號 serial,由「優先碼.順號」組成,因此輸入檔 priqueue.txt 讀入的資料經電腦編成序號如下: 序號 101 1 1999 103 3 3998 104 4 4997 204 4 4996 202 2 2995 203 3 3994 201 1 1993 301 1 1992 304 4 4991 302 2 2990 303 3 3989 就以此序號當堆積資料比較的基礎。每一類別人員建立一個如下的資料結構custNodeTag。

優先佇列 就以此序號當堆積資料比較的基礎。每一類別人員建立一個如下的資料結構 custNodeTag。 struct custNodeTag { int id; int priority; int serial; }; 其中 id 成員為人員識別碼,priority 為優先碼,serial 為序號。所建立的堆積如下圖所示。

登機人員所建立的堆積

登機人員所建立的 heap 堆積 登機人員所建立的 heap 堆積,在根節點序號 4997 是堆積裡頭最大的,他的優先碼為 4,屬於最優先的一群。移除 4997 節點後,4996 次大值變成新的根節點,接著移除,第三大值的 4991 變成新的根節點,如此循序移除,直到沒有節點為止,移除的節點資料除了顯示在螢幕之外,還輸出至 priqueue.lst 本文檔。

登機人員所建立的 heap 堆積輸出如下 id priority serial 104 4 4997 204 4 4996 104 4 4997 204 4 4996 304 4 4991 103 3 3998 203 3 3994 303 3 3989 202 2 2995 302 2 2990 101 1 1999 201 1 1993 301 1 1992

8.9 最小堆積 前面所處理的堆積為內定的最大堆積,若要建立最小堆積又如何?只要修改再堆上 ReheapUp() 及再堆下 ReheapDown() 這兩個函式就行,修改如下:

/*exchange*/ /*when child*/ /* < parent*/ void ReheapUp(struct heapTag *heap, int child) { int parent; void **data, *temp; if (child != 0) data = heap->data; parent = (child - 1)/ 2; if (heap->compare(data[child],data[parent])<0) temp=data[parent]; data[parent]=data[child]; data[child]=temp; ReheapUp (heap, parent); } return; /*exchange*/ /*when child*/ /* < parent*/

最小堆積 修改成子女節點之較小者,比父母節點小時,互換其值,小者往上升或往下降。 修改 heap.h 後的檔案改名為 minheap.h 表頭檔備用。 下列程式 minheap.c 用於測試 minheap.h 是否正確。執行時依序從整數 k[] 陣列取得下列資料建立 heap 最小堆積,輸入資料如下: 9 24 45 13 21 53 15 19 所建立的堆積如下圖所示。

最小堆積 執行時依序從整數 k[] 陣列取得下列資料建立 heap 最小堆積,輸入資料如下: 9 24 45 13 21 53 15 19