【问题描述】:前面我们实现了一个小堆的创建,及一些接口,但在实际应用中,我们并不清楚用户到底是要创建大堆还是小堆,这就要求我们的代码能够根据用户实际需要处理问题,而不是固定的只能处理单一问题。
【解决思路】:我们知道创建大堆与小堆的区别在于比较堆中左右孩子时、比较孩子节点与双亲节点时,大堆取较大者,而小堆取较小者,然后继续后面的工作。我们可以创建一个函数指针变量,保存用户传递的比较堆中元素的方法。给出两种比较方法,然后根据用户要求来分别调用,完成最终的大堆或小堆。
具体实现代码如下:
BHeapSHeap.h:
typedef int HPDatatype;
typedef int (*PCOM)(HPDatatype, HPDatatype);
int Less(HPDatatype left, HPDatatype right);//堆中元素小于比较
int Greater(HPDatatype left, HPDatatype right);//堆中元素大于比较
typedef struct Heap
{
HPDatatype* _array;
int _capacity;
int _size;
PCOM Compare;//函数指针变量,保存用户传递的比较堆中元素的方法
}Heap;
//用数组初始化堆
void InitHeap(Heap* hp, HPDatatype* array, int size,PCOM compare);
//初始化一个空堆
void InitEmptyHeap(Heap* hp, int capacity, PCOM compare);
//在堆中插入值为data的元素
void InsertHeap(Heap* hp, HPDatatype data);
//删除堆顶元素
void EraseHeap(Heap* hp);
//获取堆中有效元素个数
int HeapSize(Heap* hp);
//检测堆是否为空堆
int HeapEmpty(Heap* hp);
//获取堆顶元素
HPDatatype HeapTop(Heap* hp);
//销毁堆
void DestoryHeap(Heap* hp);
初始化堆:将用户传递的比较方法作为参数传入,并按比较方法来初始化堆。
void Swap(HPDatatype* pLeft, HPDatatype* pRight) {
HPDatatype tmp = *pLeft;
*pLeft = *pRight;
*pRight = tmp;
}
void AdjustDown(HPDatatype* array, int size, int parent, PCOM Compare) {
int child = parent * 2 + 1;//child标记了左孩子
while (child < size) {
//找双亲中较小的孩子
if (child + 1 < size && Compare(array[child + 1],array[child])) {
child = child + 1;
}
if (Compare(array[child],array[parent])) {
Swap(&array[child], &array[parent]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
int Less(HPDatatype left, HPDatatype right) {
return left < right;
}
int Greater(HPDatatype left, HPDatatype right) {
return left > right;
}
void InitHeap(Heap* hp, HPDatatype* array, int size, PCOM compare) {
assert(hp);
hp->_array = (HPDatatype*)malloc(sizeof(HPDatatype)*size);
if (hp->_array == NULL) {
assert(0);
return;
}
hp->_capacity = size;
for (int i = 0; i < size; ++i)
hp->_array[i] = array[i];
hp->_size = size;
hp->Compare = compare;
//将该完全二叉树进行调整使其满足堆的性质
//找完全二叉树中倒数第一个非叶子节点
int root = ((size - 2) >> 1);
for (; root >= 0; --root)
AdjustDown(hp->_array, size, root,hp->Compare);
}
初始化空堆:同样需要传入用户的比较方法。
void InitEmptyHeap(Heap* hp, int capacity, PCOM compare) {
assert(hp);
hp->_array = (HPDatatype*)malloc(sizeof(HPDatatype)*capacity);
if (hp->_array == NULL) {
assert(0);
return;
}
hp->Compare = compare;
hp->_capacity = capacity;
hp->_size = 0;
}
插入元素:在进行向上调整时,按照用户传入的方法比较堆中元素。
void AdjustUp(HPDatatype* array, int size, int child,PCOM Compare) {
int parent = ((child - 1) >> 1);
while (child) {
if (Compare(array[child],array[parent])) {
Swap(&array[child], &array[parent]);
child = parent;
parent = ((child - 1) >> 1);
}
else
return;
}
}
void CheckCapacity(Heap* hp) {
assert(hp);
if (hp->_size == hp->_capacity) {
int newCapacity = hp->_capacity * 2;
HPDatatype* pTemp = (HPDatatype*)malloc(sizeof(HPDatatype)*newCapacity);
if (pTemp == NULL) {
assert(0);
return;
}
for (int i = 0; i < hp->_size; ++i)
pTemp[i] = hp->_array[i];
free(hp->_array);
hp->_array = pTemp;
hp->_capacity = newCapacity;
}
}
void InsertHeap(Heap* hp, HPDatatype data) {
CheckCapacity(hp);
hp->_array[hp->_size] = data;
hp->_size++;
AdjustUp(hp->_array, hp->_size, hp->_size - 1,hp->Compare);
}
其余模块与之前小堆中的实现方法一样,此处不再赘述。下面我们来看一下最终的结果:
测试代码(小堆):
Heap hp;
int array[] = { 2,6,7,3,9,1,4,0,5,8 };
InitHeap(&hp, array, sizeof(array) / sizeof(array[0]),Less);
printf("%d\n", HeapSize(&hp));
printf("%d\n", HeapTop(&hp));
运行结果:
测试代码(大堆):
Heap hp;
int array[] = { 2,6,7,3,9,1,4,0,5,8 };
InitHeap(&hp, array, sizeof(array) / sizeof(array[0]),Greater);
printf("%d\n", HeapSize(&hp));
printf("%d\n", HeapTop(&hp));
运行结果: