前面三篇我们介绍了二叉树以及二叉树的代码实现,这篇介绍一下堆这种数据结构,是对二叉树的一个应用,堆其实是用二叉树实现的,只不过堆用到的二叉树是一种特殊的完全二叉树,这里的特殊性体现在堆中的某个节点的值总是不大于或不小于其父节点的值。根节点最大的堆叫做最大堆或大顶堆,根节点最小的堆叫做最小堆或小顶堆。完全二叉树适合用数组来存储,所以堆一般用数组来顺序存储。
下图列出了最大堆,并用数组存储的例子
最大堆数组存储结构的定义代码:
typedef struct MaxHeap{
int *data; //定义一个动态数组
int size; //元素个数
int capacity; //数组最大容量
}Heap,*MaxHeap;
最大堆添加元素:
将要添加的元素添加在数组的末尾,在堆的末尾添加完元素后可能就不满足子节点都不大于父节点这一性质,需要将新添加的元素和它的父节点进行比较,如果大于其父节点,就将该节点和父节点交换位置,该操作叫做堆的上浮操作
void Add(MaxHeap maxHeap,int e){
//数组达到给定的最大容量时扩容
if(maxHeap->size == maxHeap->capacity){
maxHeap->data = (int *) realloc(maxHeap->data,(maxHeap->capacity + INCREMENT) * sizeof(int));
if(!maxHeap->data){
printf("存储空间分配失败");
exit(-1);
}
maxHeap->capacity += INCREMENT;
}
//第一步:在数组末尾添加元素
maxHeap->data[maxHeap->size] = e;
maxHeap->size++;
//第二步:上浮操作
int insertIndex = maxHeap->size - 1; //新插入节点的索引值
while (insertIndex > 0 && maxHeap->data[insertIndex] > maxHeap->data[Parent(insertIndex)]){
int temp = maxHeap->data[insertIndex];
maxHeap->data[insertIndex] = maxHeap->data[Parent(insertIndex)];
maxHeap->data[Parent(insertIndex)] = temp;
insertIndex = Parent(insertIndex);
}
}
最大堆删除元素:
最大堆每次删除的都是堆中的最大元素,删除时先将数组下标为0的元素和数组中最后一个元素交换位置,再把数组最后一个元素删除掉。接着将数组下标为0的元素和该元素的左右子节点比较,如果小于其子节点,则交换位置,直到满足最大堆的性质,该操作叫做堆的下沉操作。
int RemoveMax(MaxHeap maxHeap){
int res = maxHeap->data[0];
if(IsMaxHeapEmpty(maxHeap)){
printf("该堆为空,无法删除元素");
exit(0);
}
//第一步:交换位置
int tem = maxHeap->data[0];
maxHeap->data[0]= maxHeap->data[maxHeap->size - 1];
maxHeap->data[maxHeap->size - 1] = tem;
//第二步:删除堆中最大元素
maxHeap->size--;
//第三步:堆的下沉操作
int k = 0;
while (LeftChild(k) < maxHeap->size){
int j = LeftChild(k);
if(j + 1 < maxHeap->size && maxHeap->data[j+1] > maxHeap->data[j])
j = RightChild(k);
if(maxHeap->data[k] >= maxHeap->data[j])
break;
int temp = maxHeap->data[k];
maxHeap->data[k] = maxHeap->data[j];
maxHeap->data[j] = temp;
k = j;
}
return res;
}
总结:
这篇实现的最大堆是二叉堆,堆还有索引堆,二项堆,斐波那契堆等,堆的典型应用就是堆排序,将会在后面的排序算法中介绍,后面将要介绍的优先队列就可以用堆来实现。接下来的两篇贴出最大堆的代码实现。