若是想了解堆的概念,能夠點擊此處查看前面關於堆的定義的隨筆。html
堆的操做接口包括初始化堆、銷燬堆、向堆中插入元素、從堆頂移除元素、堆的結點個數。數組
heap_init數據結構
void heap_init(Heap *heap,int (*compare)(const void key1,const void key2),void (*destroy)(void *data));函數
返回值:無spa
描述:初始化堆heap。指針
在對堆執行其餘操做以前,必需要對其進行初始化。code
函數compare用來比較堆中的結點大小,若是堆爲最大值堆,當key1>key2時,函數返回1;當key1=key2時,函數返回0;當key1<key2時,函數返回-1。在最小值堆中返回值正好相反。htm
函數指針destroy,經過調用heap_deatroy來釋放動態分配的內存空間。若是堆中的數據不須要釋放,那麼destroy應該指向NULL。blog
複雜度:O(1)排序
heap_destroy
void heap_destroy(Heap *heap);
返回值:無
描述:銷燬堆heap。
在調用heap_destroy以後再也不容許進行其餘操做。
heap_destroy會刪除堆中的全部結點,在刪除的同時調用heap_init中的destroy所指向的銷燬函數(此函數指針不爲NULL)。
複雜度:O(n),n表明堆中的結點個數。
heap_insert
int heap_insert(Heap *heap,const void *data);
返回值:若是插入元素成功則返回0,不然返回-1。
描述:向堆heap中插入一個結點。
新結點包含一個指向data的指針,只要結點仍然存在於堆中,此指針就一直有效。與data有關的內存空間將由函數的調用者來管理。
複雜度:O(lg n),n表明堆中的結點個數。
heap_extract
int heap_extract(Heap *heap,void **data);
返回值:若是結點釋放成功則返回0,不然返回-1。
描述:從堆heap中釋放堆頂部的結點。
返回時,data指向被釋放結點中存儲的數據。與data相關的內存空間將由函數的調用者來管理。
複雜度:O(lg n),n表明堆中的結點個數。
heap_size
int heap_size(const Heap *heap);
返回值:堆中結點的個數。
描述:這是一個獲取堆heap中結點個數的宏。
複雜度:O(1)。
下面來分析一下如何實現堆:
咱們用來叉樹來實現堆,將其結點按照樹的層次結構存放在一個數組中。咱們令Heap做爲堆的數據結構,此結構體包含4個成員:
一、size指明堆中的結點個數;二、compare與三、destroy是用於封閉傳入heap_init的函數的指針;四、tree是堆中存儲結點的數組。
/*heap.h* 堆的頭文件/ #ifndef HEAP_H #define HEAP_H /*定義堆的數據結構體*/ typedef struct Heap_ { int size; int (*compare)(const void *key1,const void *key2); void (*destroy)(void *data); void **tree; }Heap; /*公共接口部分*/ void heap_init(Heap *heap,int (*compare)(const void *key1,const void key2),void (*destroy)(void *data)); void heap_destroy(Heap *heap); int heap_insert(Heap *heap,const void *data); int heap_extract(Heap *heap,void **data); #define heap_size(heap)((heap)->size) #endif // HEAP_H
/*heap.c*/ #include <stdlib.h> #include <string.h> #include "heap.h" /*定義heap執行中須要使用的私有宏*/ #define heap_parent(npos) ((int)(((npos)-1)/2)) /*npos的父結點*/ #define heap_left(npos) (((npos)*2)+1) /*npos的左兄弟結點*/ #define heap_right(npos) (((npos)*2)+2) /*npos的右兄弟結點*/ /*heap_init 堆的初始化*/ void heap_init(Heap *heap,int (*compare)(void *key1,void *key2),void (*destroy)(void *data)) { /*只須要將size設爲0,destroy成員指向destroy,將tree指針設置爲NULL*/ heap->size = 0; heap->compare = compare; heap->destroy = destroy; heap->tree = NULL; return ; } /*heap_destroy 銷燬堆*/ void heap_destroy(Heap *heap) { int i; /*移除堆中全部的結點*/ if(heap->destroy != NULL) { for(i=0; i<heap_size(heap);i++) { /*調用用戶自定義函數釋放動態分配的數據*/ heap->destroy(heap->tree[i]); } } /*釋放爲堆分配的空間*/ free(heap->tree); memset(heap,0,sizeof(Heap)); return; } /*heap_insert 向堆中插入結點*/ int heap_insert(Heap *heap,const void *data) { void *temp; int ipos; ppos; /*爲結點分配空間*/ if((temp = (void **)realloc(heap->tree,(heap->size(heap)+1)*sizeof(void *))) == NULL) { return -1; } else { heap->tree = temp; } /*將結點插入到堆的最末端*/ heap->tree[heap_size(heap)] = (void *)data; /*將新結點向上推進,恢復堆的排序特色*/ ipos = heap_size(heap); /*堆結點數的數值*/ ppos = heap_parent(ipos); /*ipos位置結點的父結點*/ /*若是堆不爲空,而且末位結點大於其父結點,則將兩個結點進行交換*/ while(ipos>0 && heap->compare(heap->tree[ppos],heap->tree[ipos])<0) { /*交換末端結點與其父結點的位置*/ temp = heap->tree[ppos]; heap->tree[ppos] = heap->tree[ipos]; heap->tree[ipos] = temp; /*將定位結點向上移動一層,以繼續執行堆排序*/ ipos = ppos; ppos = heap_parent(ipos); } /*堆插入與排序完成,調整堆的結點數量值*/ heap->size++; return 0; } /*heap_extract 釋放堆頂部的結點*/ int heap_extract(Heap *heap,void **data) { void *save, *temp; int ipos,lpos,rpos,mpos; /*不容許從空的堆中釋放結點*/ if(heap->size(heap) == 0) return -1; /*釋放堆頂部的結點*/ /*首先將data指向將要釋放結點的數據*/ *data = heap->tree[0] /*將save指向未位結點*/ save = heap->tree[heap_size(heap)-1]; if(heap_size(heap)-1 > 0) { /*爲堆分配一個稍小一點的空間*/ if((temp = (void **)realloc(heap->tree,(heap_size(heap)-1)*sizeof(void *)))==NULL) { return -1; } else { heap->tree = temp; } /*調整堆的大小*/ heap->size--; } else { /*只有一個結點,釋放並從新管理堆,並返回*/ free(heap->tree); heap->tree = NULL; heap->size = 0; return 0; } /*將末位結點拷貝到根結點中*/ heap->tree[0] = save; /*從新調整樹的結構*/ ipos = 0; /*頂元素*/ lpos = heap_left(ipos); /*左子結點*/ rpos = heap_right(ipos); /*右子結點*/ /*父結點與兩個子結點比較、交換,直到再也不須要交換爲止,或者結點到達一個葉子位置*/ while(1) { /*選擇子結點與當前結點進行交換*/ lpos = heap_left(ipos); rpos = heap_right(ipos); /*父結點與左子結點位置不正確,左子結點大於其父結點*/ if(lpos < heap_size(heap) && heap->compare(heap->tree[lpos],heap->tree[ipos])>0) { mpos = lpos; /*將左子結點的位置賦給mpos(最大位置)*/ } else { mpos = ipos; } if(rpos < heap_size(heap) && heap->compare(heap->tree[rpos],heap->tree[mpos])>0) { mpos = rpos; } /*當mpos和ipos相等時,堆特性已經被修復,結束循環*/ if(mpos == ipos) { break; } else { /*交換當前結點與被選中的結點的內容*/ temp = heap->tree[mpos]; heap->tree[mpos] = heap->tree[ipos]; heap->tree[ipos] = temp; /*下移一層,以繼續執行堆排序*/ ipos = mpos; } } return 0; }