目录
1、什么是堆
一颗完全二叉树且其任意节点都比其孩子节点大(小),将这种数据结构成为大根堆(小根堆)。
2、堆有什么性质
- 堆中某个结点的值总是不大于或者不小于其父节点的值
- 堆必定是一颗完全二叉树
小根堆存储结构 大根堆存储结构
如图,最上边的元素称为堆顶,易知:
- 堆顶的元素要么是最大的,要么是最小的
- 每一条路径一定事升序或者降序
3、堆的实现
3.1、堆向下调整算法
现在我们给出一个数组,逻辑上是一颗完全二叉树,我们通过从根节点开始的向下调整算法可以把它调整为一个小堆。注意:向下调整算法有一个前提——左右子树必须是一个堆,才能调整。
如图,以27为堆顶的左右子树都是小堆,只有根节点不满足,这种情况就可以通过将根节点向下调整到合适的位置来形成堆。
- 让27和它较小的一个孩子进行交换,即27和15进行交换
- 再让27和它较小的孩子进行交换,即27和18进行交换
- 再让27和它较小的孩子进行交换,即27和25进行交换
- 此时,形成堆——如下图
3.2堆的创建
和上边同理,若给的数据,初始化堆时,左右子树都不是堆的结构,则需要,每次调整一次,都要对其下的子树进行新的检查和调整。
// 堆的构建
//创建堆的时候需要用到向下调整
void HeapCreate(Heap* hp, HPDataType* array, int size,PFUNC pCompare)
{
assert(hp);
//1、将hp指向的堆结构体初始化好
hp->array = (HPDataType*)malloc(sizeof(HPDataType) * size);
if (hp->array == NULL)
{
assert(0);
return;
}
hp->capacity = size;
//2、将数组中的元素往堆中存放
memcpy(hp->array, array, size * sizeof(HPDataType));
hp->size = size;
hp->pCompare = pCompare;
//3、需要对hp->array中的元素进行调整
//BubbleSort(hp->array, size);——方法可以,但不够好,时间复杂度太高
for (int root = (size - 2) / 2; root >= 0;root--)
AdjustDown(hp, root);
}
//堆的向下调整
void AdjustDown(Heap*hp, int parent)
{
int child = parent*2+1;
int size = hp->size;
while (child < size)
{
//1、找parent较小的孩子,先默认让parent = 他的左孩子,因为是完全二叉树,很有可能parent有左孩子没有右孩子
if (child + 1 < size && hp->pCompare(hp->array[child+1],hp->array[child]))
{
child += 1;
}
//2、检测parent是否满足堆的特性
if (hp->pCompare(hp->array[child], hp->array[parent]))
{
Swap(&hp->array[parent], &hp->array[child]);
//大的元素往下走,可能导致子树也不满足堆的性质
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
3.3堆的插入
// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
//1、将要插入的元素放入堆所在的数组中
CheckCapicity(hp);//扩容方法
hp->array[hp->size] = x;
hp->size++;
//2、判断新节点与其父节点是否满足堆的特性,不满足的话,需要进行调整
Adjustup(hp, hp->size - 1);
}
void CheckCapicity(Heap* hp)
{
assert(hp);
if (hp->size == hp->capacity)
{
int newCapicity = hp->capacity * 2;
HPDataType* tem = (HPDataType*)malloc(sizeof(HPDataType) * newCapicity);
if (NULL == tem)
{
assert(0);
return;
}
memcpy(tem, hp->array, sizeof(HPDataType) * hp->size);
free(hp->array);
hp->array = tem;
hp->capacity = newCapicity;
}
}
void Adjustup(Heap*hp, int child)
{
int parent = (child - 1) / 2;
while (child)
{
if (hp->pCompare(hp->array[parent],hp->array[child]))
{
Swap(&hp->array[parent], &hp->array[child]);
child = parent;
parent = (child - 1) / 2;
}
else
return;
}
}
3.4堆的删除
堆删除时删除的是堆顶元素。
// 堆的删除
void HeapPop(Heap* hp)
{
assert(hp);
if (HeapEmpty(hp))
return;
//1、将堆顶元素与堆中最后一个元素进行交换
Swap(&hp->array[0], &hp->array[hp->size - 1]);
//2、将堆中有效元素减少一个
hp->size--;
//3、将堆顶元素往下调整
AdjustDown(hp, 0);
}