堆的C语言实现——堆与堆排序(二)
堆的C語言實現——堆與堆排序(二)
- 堆的C語言實現——堆與堆排序(二)
- 頭文件
- 初始化函數
- 下濾函數1(遞歸)
- 堆構造函數1(自底向上,遞歸)
- 下濾函數2(非遞歸,交換法)
- 下濾函數3(非遞歸,空穴法)
- 堆構造函數2(自底向上,非遞歸,空穴法)
- 自頂向下堆構造
- 插入函數1(交換法)
- 插入函數2(空穴法)
- 堆構造函數3(自頂向下,空穴法)
- 刪除最大鍵函數
- 完整的代碼及運行截圖
上一篇博文 堆與堆排序(一)介紹了什么是堆、堆的性質、堆的構造。這篇博文我們以大根堆為例,討論堆的C語言實現。完整的代碼會附在本文末尾。
頭文件
#ifndef _MAX_HEAP_ #define _MAX_HEAP_#define MAX_HEAP_SENTINEL 100 #define MIN_PQ_CAPACITY 3 #define HEAP_CAPACITY 20#define LEFT(i) (2*i) #define RIGHT(i) (2*i+1) #define PARENT(i) (i/2)typedef int element_t;struct heap_struct {int capacity;int size;element_t *elements; };typedef struct heap_struct *priority_queue;void print_array(int a[], int len); void print_heap(priority_queue heap);// 基于空穴法的下濾函數(非遞歸) void percolate_down_no_swap(int a[], int n, int t);// 基于遞歸的下濾函數(交換法) void percolate_down_recursive(int a[], int n, int t);// 基于交換法的下濾函數(非遞歸) void percolate_down(int a[], int n, int t);priority_queue initialize(int capacity);//優先隊列初始化// 基于空穴法的插入 void insert_no_swap(element_t x, priority_queue max_heap); void insert(element_t x, priority_queue max_heap); //基于交換法的插入 int is_full(priority_queue max_heap); int is_empty(priority_queue max_heap); element_t delete_max(priority_queue max_heap); // 刪除最大節點// 自底向上的堆構造(遞歸),調用了函數 percolate_down_recursive priority_queue build_max_heap_bottom_up_recursive(element_t a[], int elmt_cnt); // 自底向上的堆構造(非遞歸),調用了函數 percolate_down_no_swap priority_queue build_max_heap_bottom_up(element_t a[], int elmt_cnt); // 自頂向下的堆構造(非遞歸),調用了函數 insert_no_swap priority_queue build_max_heap_up_bottom(element_t a[], int elmt_cnt); // 自頂向下的堆構造(非遞歸),調用了函數 insert priority_queue build_max_heap_up_bottom2(element_t a[], int elmt_cnt);void destroy(priority_queue heap); // 用來釋放內存 #endif第4行:#define MAX_HEAP_SENTINEL 100
在上一篇博文中說過,可以用數組來實現堆。方法是用從上到下、從左到右的方式來記錄堆的元素。為了方便起見,可以在這種數組從 1 到 n 的位置上存放堆的元素,留下H[0],要么讓它空著,要么在其中放一個限位器,它的值大于堆中任何一個元素。 所以我們定義了一個限位器 MAX_HEAP_SENTINEL,假設最大堆的任何一個元素都小于100.
14~19行:定義了一個結構體struct heap_struct
struct heap_struct {int capacity;int size;element_t *elements; };capacity表示堆的容量,也就是說節點個數最多就是 capacity,不能再多了。
size表示當前堆的大小,即當前這個堆有多少個節點。
elements是一個指針,指向數組的第一個元素(在初始化函數中會體現這一點)。
初始化函數
priority_queue initialize(int capacity) {priority_queue heap;if(capacity < MIN_PQ_CAPACITY){printf("Priority queue capacity is too small\n");exit(EXIT_FAILURE);}heap = malloc( sizeof(struct heap_struct));if(heap == NULL){printf("out of space\n");exit(EXIT_FAILURE);}// +1 是因為要包含[0]heap->elements = malloc( (capacity + 1) * sizeof(element_t) );if(heap->elements == NULL){printf("out of space\n");exit(EXIT_FAILURE);}heap->capacity = capacity;heap->size = 0; #ifdef MAX_HEAP_SENTINEL// MAX_HEAP_SENTINEL 大于任何一個節點值,作為標記heap->elements[0] = MAX_HEAP_SENTINEL; #endifreturn heap; }第11行:為結構體分配內存 heap = malloc( sizeof(struct heap_struct));
第19行:為數組分配內存 heap->elements = malloc( (capacity + 1) * sizeof(element_t) );
第27行:heap->size = 0;
初始化狀態,堆的元素數為0,即堆為空。
下濾函數1(遞歸)
// 下濾函數(遞歸解法) // 假定以 LEFT(t) 和 RIGHT(t) 為根的子樹都已經是大根堆 // 調整以 t 為根的子樹,使之成為大根堆。 // 節點位置為 1~n,a[0]不使用 void percolate_down_recursive(int a[], int n, int t) { #ifdef PRINT_PROCEDUREprintf("check %d\n", t); #endifint left = LEFT(t);int right = RIGHT(t); int max = t; //假設當前節點的鍵值最大if(left <= n) // 說明t有左孩子 {max = a[left] > a[max] ? left : max;}if(right <= n) // 說明t有右孩子 {max = a[right] > a[max] ? right : max;}if(max != t){ swap(a + max, a + t); // 交換t和它的某個孩子,即t下移一層 #ifdef PRINT_PROCEDUREprintf("%d NOT satisfied, swap it and %d \n",t, max); #endifpercolate_down_recursive(a, n, max); // 遞歸,繼續考察t} }上一篇博文已經說了,對于自底向上構造堆,基本思想如下:
如果該節點不滿足父母優勢,就把該節點的鍵 K 和它子女的最大鍵進行交換,然后再檢查在新的位置上,K 是否滿足父母優勢要求。這個過程一直繼續到對 K 的父母優勢要求滿足為止——這種策略叫做下濾(percolate down)。
此函數用來實現第3步。有了這個函數,我們就可以寫出自底向上構造堆的完整代碼了。
堆構造函數1(自底向上,遞歸)
// element_t a[]是用戶數組 例如 int a[5]={1,3,5,2,4}; // 則傳參 a 和 5 priority_queue build_max_heap_bottom_up_recursive(element_t a[], int elmt_cnt) {priority_queue pq = initialize(elmt_cnt);assert(pq != NULL);// 把用戶數組拷貝到堆中memcpy(pq->elements+1, a, sizeof(element_t)*elmt_cnt);pq->size = elmt_cnt; int i;// 從最后一個父母節點開始下濾,使成為大根堆for(i = elmt_cnt/2; i >= 1; --i){ percolate_down_recursive(pq->elements, elmt_cnt, i);}return pq; }percolate_down_recursive函數用了遞歸,雖然很容易理解,但是對自己要求高的程序員看著有點礙眼,能否不用遞歸呢?當然能,只是要犧牲一點可讀性。
下濾函數2(非遞歸,交換法)
// 此函數沒有用遞歸 void percolate_down(int a[], int n, int t) {int key = a[t]; // 用key記錄當前節點的鍵值int max_idx;int heap_ok = 0; // 初始條件是父母優勢不滿足 #ifdef PRINT_PROCEDURE printf("check %d\n", t); #endif // LEFT(t) <= n 成立說明有孩子while(!heap_ok && (LEFT(t) <= n)){ max_idx = LEFT(t); // 假設左孩子鍵值最大if(LEFT(t) < n) // 條件成立則說明有2個孩子{if(a[LEFT(t)] < a[RIGHT(t)])max_idx = RIGHT(t); //說明右孩子的鍵值比左孩子大}//此時max_idx指向鍵值最大的孩子if(a[t] >= a[max_idx]){heap_ok = 1; //滿足父母優勢,跳出循環,本函數返回}else{ //交換位置,a[t]被換到了a[max_idx]的位置swap(a+t, a+max_idx); #ifdef PRINT_PROCEDURE printf("%d NOT satisfied, swap it and %d \n",t, max_idx);printf("check %d\n", t); #endif t = max_idx; //繼續考察a[max_idx]是否滿足父母優勢 } } return; }第27行:交換函數的代碼是
//交換*a和*b, 內部函數 static void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp; }可以看到,交換需要3個賦值語句,還是比較費時間的。這里介紹一種“空穴法”,可以避免交換。
如下圖所示,1號節點的左子樹和右子樹都已經是大根堆,這時候需要對1號節點下濾,使以1號節點為根的樹成為一個大根堆。
下濾函數3(非遞歸,空穴法)
// 非遞歸且不用交換 void percolate_down_no_swap(int a[], int n, int t) {int key = a[t]; // 用key記錄鍵值int max_idx;int heap_ok = 0; // 初始條件是父母優勢不滿足 #ifdef PRINT_PROCEDURE printf("check %d\n", t); #endif // LEFT(t) <= n 成立則說明 t 有孩子while(!heap_ok && (LEFT(t) <= n)){ max_idx = LEFT(t); // 假設左右孩子中,左孩子鍵值較大if(LEFT(t) < n) // 條件成立則說明有2個孩子{if(a[LEFT(t)] < a[RIGHT(t)])max_idx = RIGHT(t); //說明右孩子的鍵值比左孩子大}//此時max_idx指向鍵值較大的孩子if(key >= a[max_idx]){heap_ok = 1; //為 key 找到了合適的位置,跳出循環}else{ a[t] = a[max_idx]; //孩子上移一層,max_idx 被空出來,成為空穴 #ifdef PRINT_PROCEDURE printf("use %d fill %d \n", max_idx, t);printf("%d is empty\n", max_idx); #endif t = max_idx; //令 t 指向空穴 } }a[t] = key; // 把 key 填入空穴 #ifdef PRINT_PROCEDURE printf("use value %d fill %d \n", key, t); #endif return; }堆構造函數2(自底向上,非遞歸,空穴法)
有了上面這個函數,在構造大根堆時,函數可以這樣寫:
priority_queue build_max_heap_bottom_up(element_t a[], int elmt_cnt) {priority_queue pq = initialize(elmt_cnt);assert(pq != NULL);memcpy(pq->elements+1, a, sizeof(element_t)*elmt_cnt);pq->size = elmt_cnt;int i;for(i = elmt_cnt/2; i >= 1; --i){ // 下濾,非遞歸,空穴法percolate_down_no_swap(pq->elements, elmt_cnt, i);}return pq; }自頂向下堆構造
除了上面的構造方法,還有一種算法(效率較低)是通過把新的鍵連續插入預先構造好的堆,來構造一個新堆。有的人把它稱作自頂向下堆構造。
這種策略叫做上濾(percolate up)。
以4,1,3,2,16,9,10,14,8,7這列鍵為例,用圖說明上濾的過程。
插入函數1(交換法)
void insert(element_t x, priority_queue max_heap) {if( is_full(max_heap) ){printf("priority queue is full, insert failed\n");return;}// 把新節點附加在當前堆的最后一個葉子后面max_heap->elements[ ++max_heap->size ] = x; int cur = max_heap->size; // cur 指向新的節點 #ifdef MAX_HEAP_SENTINELwhile( x > max_heap->elements[ cur/2 ] ) // 把新節點的鍵值和它的父母比較 #else// 當 cur == 1 時,說明新節點已經被交換到了樹根的位置,此時應該跳出循環while( (cur != 1) && (x > max_heap->elements[ cur/2 ]) ) #endif{// 交換新節點和它的父母swap(max_heap->elements + cur/2, max_heap->elements + cur);cur /= 2; // cur繼續指向新節點} }12~17行:這里用了兩種寫法,如果沒有使用限位器MAX_HEAP_SENTINEL,那么 elements[0]就沒有使用,當cur == 1的時候,說明節點已經被交換到了根的位置,它沒有父母節點,這時候應該停止操作。所以在while的條件判斷中加入了(cur != 1).
如果使用了限位器,elements[0]中的值是一個比任何節點都大的值。當cur == 1的時候,說明節點已經被交換到了根的位置,這時候elements[ cur/2 ]就是elements[0],顯然while的循環條件不成立,所以循環中止。
同前文一樣,swap操作比較費時,函數還可以優化。
插入函數2(空穴法)
// swap操作比較費時,上面的函數可以優化一下void insert_no_swap(element_t x, priority_queue max_heap) {if( is_full(max_heap) ){printf("priority queue is full, insert failed\n");return;}// 在當前堆的最后一個葉子后面創建一個空穴,用cur指向空穴int cur = ++max_heap->size; #ifdef MAX_HEAP_SENTINELwhile( x > max_heap->elements[ cur/2 ] ) // 新節點的鍵值和空穴的父母作比較 #elsewhile( (cur != 1) && (x > max_heap->elements[ cur/2 ]) ) #endif{// 父母向下移動一層,填補空穴,原父母的位置成為新的空穴max_heap->elements[cur] = max_heap->elements[cur/2];cur /= 2; }max_heap->elements[cur] = x; //把新節點填入空穴 }堆構造函數3(自頂向下,空穴法)
有了插入函數,自頂向下構造堆的函數就不難寫了。
priority_queue build_max_heap_up_bottom(element_t a[], int elmt_cnt) {priority_queue pq = initialize(elmt_cnt);assert(pq != NULL);int i;for(i = 0; i < elmt_cnt; ++i){ insert_no_swap(a[i], pq); #ifdef PRINT_PROCEDUREprint_heap(pq); #endif}return pq; }刪除最大鍵函數
找出最大的鍵是容易的,困難的是刪除它。當刪除一個最大鍵時,在根位置產生了一個空穴。由于現在堆少了一個元素,因此堆中最后一個元素X必須移動到某個地方。如果X可以被放到空穴中(滿足父母優勢),那么刪除完成。不過這一般不太可能,因此我們將空穴的兩個兒子中較大者移入空穴,這樣就把空穴向下推了一層。重復該步驟直到X可以被放入空穴中。
因為“用空穴法進行下濾”的詳細方法在上一篇博文中講過,所以這里略去了下濾的詳細過程。
假設已經構造好了一個大根堆,對應的數組是
[16,14,10,8,7,3,9,1,4,2]
下面的一系列圖用來演示如何逐個刪除最大鍵。
element_t delete_max(priority_queue max_heap) {if( is_empty( max_heap ) ){printf("priority queue is empty, return [0]\n");return max_heap->elements[0];}if( max_heap->size == 1){max_heap->size = 0;return max_heap->elements[1];}else{ element_t max_elmt = max_heap->elements[1];// 得到最后一個元素,并且堆的大小減一element_t last_elmt = max_heap->elements[max_heap->size--];// 把最后一個元素移動到樹根的位置max_heap->elements[1] = last_elmt;// 調整樹根percolate_down_no_swap(max_heap->elements,max_heap->size ,1); return max_elmt;} }寫到這里,重要的函數就講完了。
完整的代碼及運行截圖
最后附上完整的代碼和測試函數,對了,還有運行截圖。
/* max_heap.h */ #ifndef _MAX_HEAP_ #define _MAX_HEAP_#define MAX_HEAP_SENTINEL 100 #define MIN_PQ_CAPACITY 3 #define HEAP_CAPACITY 20#define LEFT(i) (2*i) #define RIGHT(i) (2*i+1) #define PARENT(i) (i/2)typedef int element_t;struct heap_struct {int capacity;int size;element_t *elements; };typedef struct heap_struct *priority_queue;void print_array(int a[], int len); void print_heap(priority_queue heap);void percolate_down_no_swap(int a[], int n, int t); void percolate_down_recursive(int a[], int n, int t); void percolate_down(int a[], int n, int t);priority_queue initialize(int capacity); void insert_no_swap(element_t x, priority_queue max_heap); void insert(element_t x, priority_queue max_heap); int is_full(priority_queue max_heap); int is_empty(priority_queue max_heap); element_t delete_max(priority_queue max_heap);priority_queue build_max_heap_bottom_up_recursive(element_t a[], int elmt_cnt); priority_queue build_max_heap_bottom_up(element_t a[], int elmt_cnt); priority_queue build_max_heap_up_bottom(element_t a[], int elmt_cnt); priority_queue build_max_heap_up_bottom2(element_t a[], int elmt_cnt); void destroy(priority_queue heap); #endif /* max_heap.c */ #include "max_heap.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> void print_heap(priority_queue heap) {if(heap->size == 0){printf("the priority queue is empty\n");return;}// +1是因為heap->size不包括elements[0]print_array(heap->elements, heap->size + 1); }// 打印下標和元素值的通用函數 void print_array(int a[], int len) {printf("index:\n");int i;for (i = 0; i < len; ++i) {printf("%3d ", i); //寬度3加上空格就是4,所以后面有 4 * len} printf("\n");for (int i = 1; i <= 4 * len; ++i) {printf("%s", "-");} printf("\n");// 以上2個for循環用于打印數組下標和分隔線“-----------”for(i=0; i<len; ++i)printf("%3d ",a[i]); //打印數組的值printf("\n\n"); }//交換*a和*b, 內部函數 static void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp; }priority_queue initialize(int capacity) {priority_queue heap;if(capacity < MIN_PQ_CAPACITY){printf("Priority queue capacity is too small\n");exit(EXIT_FAILURE);}heap = malloc( sizeof(struct heap_struct));if(heap == NULL){printf("out of space\n");exit(EXIT_FAILURE);}// +1 是因為要包含[0]heap->elements = malloc( (capacity + 1) * sizeof(element_t) );if(heap->elements == NULL){printf("out of space\n");exit(EXIT_FAILURE);}heap->capacity = capacity;heap->size = 0; #ifdef MAX_HEAP_SENTINEL// MAX_HEAP_SENTINEL 大于任何一個節點值,作為標記heap->elements[0] = MAX_HEAP_SENTINEL; #endifreturn heap; }void destroy(priority_queue heap) // 別忘了釋放內存! {free(heap->elements);free(heap); }int is_full(priority_queue max_heap) {return (max_heap->size == max_heap->capacity); }int is_empty(priority_queue max_heap) {return (max_heap->size == 0); }// 下濾函數(遞歸解法) // 假定以 LEFT(t) 和 RIGHT(t) 為根的子樹都已經是大根堆 // 調整以 t 為根的子樹,使之成為大根堆。 // 節點位置為 1~n,a[0]不使用 void percolate_down_recursive(int a[], int n, int t) { #ifdef PRINT_PROCEDUREprintf("check %d\n", t); #endifint left = LEFT(t);int right = RIGHT(t); int max = t; //假設當前節點的鍵值最大if(left <= n) // 說明t有左孩子 {max = a[left] > a[max] ? left : max;}if(right <= n) // 說明t有右孩子 {max = a[right] > a[max] ? right : max;}if(max != t){ swap(a + max, a + t); // 交換t和它的某個孩子,即t下移一層 #ifdef PRINT_PROCEDUREprintf("%d NOT satisfied, swap it and %d \n",t, max); #endifpercolate_down_recursive(a, n, max); // 遞歸,繼續考察t} }// 2018-5-4 8:17 // 此函數沒有用遞歸 void percolate_down(int a[], int n, int t) {int key = a[t]; // 用key記錄當前節點的鍵值int max_idx;int heap_ok = 0; // 初始條件是父母優勢不滿足 #ifdef PRINT_PROCEDURE printf("check %d\n", t); #endif // LEFT(t) <= n 成立說明有孩子while(!heap_ok && (LEFT(t) <= n)){ max_idx = LEFT(t); // 假設左孩子鍵值最大if(LEFT(t) < n) // 條件成立則說明有2個孩子{if(a[LEFT(t)] < a[RIGHT(t)])max_idx = RIGHT(t); //說明右孩子的鍵值比左孩子大}//此時max_idx指向鍵值最大的孩子if(a[t] >= a[max_idx]){heap_ok = 1; //滿足父母優勢,跳出循環,本函數返回}else{ //交換位置,a[t]被換到了a[max_idx]的位置swap(a+t, a+max_idx); #ifdef PRINT_PROCEDURE printf("%d NOT satisfied, swap it and %d \n",t, max_idx);printf("check %d\n", t); #endif t = max_idx; //繼續考察a[max_idx]是否滿足父母優勢 } } return; }// 非遞歸且不用交換 void percolate_down_no_swap(int a[], int n, int t) {int key = a[t]; // 用key記錄鍵值int max_idx;int heap_ok = 0; // 初始條件是父母優勢不滿足 #ifdef PRINT_PROCEDURE printf("check %d\n", t); #endif // LEFT(t) <= n 成立則說明 t 有孩子while(!heap_ok && (LEFT(t) <= n)){ max_idx = LEFT(t); // 假設左右孩子中,左孩子鍵值較大if(LEFT(t) < n) // 條件成立則說明有2個孩子{if(a[LEFT(t)] < a[RIGHT(t)])max_idx = RIGHT(t); //說明右孩子的鍵值比左孩子大}//此時max_idx指向鍵值較大的孩子if(key >= a[max_idx]){heap_ok = 1; //為 key 找到了合適的位置,跳出循環}else{ a[t] = a[max_idx]; //孩子上移一層,max_idx 被空出來,成為空穴 #ifdef PRINT_PROCEDURE printf("use %d fill %d \n", max_idx, t);printf("%d is empty\n", max_idx); #endif t = max_idx; //令 t 指向空穴 } }a[t] = key; // 把 key 填入空穴 #ifdef PRINT_PROCEDURE printf("use value %d fill %d \n", key, t);#endif return; }void insert(element_t x, priority_queue max_heap) {if( is_full(max_heap) ){printf("priority queue is full, insert failed\n");return;}// 把新節點附加在當前堆的最后一個葉子后面max_heap->elements[ ++max_heap->size ] = x; int cur = max_heap->size; // cur 指向新的節點 #ifdef MAX_HEAP_SENTINELwhile( x > max_heap->elements[ cur/2 ] ) // 把新節點的鍵值和它的父母比較 #else// 當 cur == 1 時,說明新節點已經被交換到了樹根的位置,此時應該跳出循環while( (cur != 1) && (x > max_heap->elements[ cur/2 ]) ) #endif{// 交換新節點和它的父母swap(max_heap->elements + cur/2, max_heap->elements + cur);cur /= 2; // cur繼續指向新節點}}// swap操作比較費時,上面的函數可以優化一下void insert_no_swap(element_t x, priority_queue max_heap) {if( is_full(max_heap) ){printf("priority queue is full, insert failed\n");return;}// 在當前堆的最后一個葉子后面創建一個空穴,用cur指向空穴int cur = ++max_heap->size; #ifdef MAX_HEAP_SENTINELwhile( x > max_heap->elements[ cur/2 ] ) // 新節點的鍵值和空穴的父母作比較 #elsewhile( (cur != 1) && (x > max_heap->elements[ cur/2 ]) ) #endif{// 父母向下移動一層,填補空穴,原父母的位置成為新的空穴max_heap->elements[cur] = max_heap->elements[cur/2];cur /= 2; }max_heap->elements[cur] = x; //把新節點填入空穴}// element_t a[]是用戶數組 例如 int a[5]={1,3,5,2,4}; // 則傳參 a 和 5 priority_queue build_max_heap_bottom_up_recursive(element_t a[], int elmt_cnt) {priority_queue pq = initialize(elmt_cnt);assert(pq != NULL);memcpy(pq->elements+1, a, sizeof(element_t)*elmt_cnt);pq->size = elmt_cnt; int i;// 從最后一個父母節點開始堆化for(i = elmt_cnt/2; i >= 1; --i){ percolate_down_recursive(pq->elements, elmt_cnt, i);}return pq; }priority_queue build_max_heap_bottom_up(element_t a[], int elmt_cnt) {priority_queue pq = initialize(elmt_cnt);assert(pq != NULL);memcpy(pq->elements+1, a, sizeof(element_t)*elmt_cnt);pq->size = elmt_cnt;int i;for(i = elmt_cnt/2; i >= 1; --i){ percolate_down_no_swap(pq->elements, elmt_cnt, i);}return pq; }priority_queue build_max_heap_up_bottom(element_t a[], int elmt_cnt) {priority_queue pq = initialize(elmt_cnt);assert(pq != NULL);int i;for(i = 0; i < elmt_cnt; ++i){ insert_no_swap(a[i], pq); #ifdef PRINT_PROCEDUREprint_heap(pq); #endif}return pq; }//和上一個函數的區別是插入采用交換法 priority_queue build_max_heap_up_bottom2(element_t a[], int elmt_cnt) {priority_queue pq = initialize(elmt_cnt);int i;for(i = 0; i < elmt_cnt; ++i){ insert(a[i], pq);}return pq; }element_t delete_max(priority_queue max_heap) {if( is_empty( max_heap ) ){printf("priority queue is empty, return [0]\n");return max_heap->elements[0];}if( max_heap->size == 1){max_heap->size = 0;return max_heap->elements[1];}else{ element_t max_elmt = max_heap->elements[1];// 得到最后一個元素,并且大小減一element_t last_elmt = max_heap->elements[max_heap->size--];// 把最后一個元素移動到樹根的位置max_heap->elements[1] = last_elmt;// 調整樹根percolate_down_no_swap(max_heap->elements,max_heap->size ,1);return max_elmt;} }//測試函數 int main(void) {int a[]={4,1,3,2,16,9,10,14,8,7}; //10個int elmt_cnt = sizeof(a) / sizeof(a[0]);priority_queue pq1,pq2,pq3,pq4;// 測試最大堆構造函數pq1 = build_max_heap_bottom_up_recursive(a,elmt_cnt);pq2 = build_max_heap_bottom_up(a,elmt_cnt); pq3 = build_max_heap_up_bottom(a,elmt_cnt);pq4 = build_max_heap_up_bottom2(a,elmt_cnt);// 打印構造結果print_heap(pq1);print_heap(pq2);print_heap(pq3);print_heap(pq4);// 測試刪除最大鍵的函數int del_key = 0;printf("delete the max key: ");while(is_empty(pq3)==0) // 用pq3測試{del_key = delete_max(pq3);printf("%d ", del_key);}printf("\n");destroy(pq1); //別忘了釋放內存!destroy(pq2);destroy(pq3);destroy(pq4); return 0; }總結
以上是生活随笔為你收集整理的堆的C语言实现——堆与堆排序(二)的全部內容,希望文章能夠幫你解決所遇到的問題。