堆是一颗完全二叉树,所有元素是按照完全二叉树的顺序存储方式放在一个数组,堆中每个结点都比其孩子结点小(大),叫小(大)堆
所以,堆的底层是按数组方式来存储,数组元素可能有序,可能无序,但堆中元素是有顺序的,要么是小堆,要么是大堆。
下图是数组对应的完全二叉树,但还不是堆,所以在创建堆时,需要对数组进行向下调整,建成小堆或大堆
- 建堆(这里我建的小堆,你也可以建大堆,后面会介绍如何一起来建大小堆)
如果从第一个节点调整,不能保证向下调整后,堆顶元素最小,因此不能满足堆的特性,只能看出,双亲结点小于孩子节点,所以在调整时,需要从倒数第一个非叶子节点(size-1-1)/2开始,依次往上
void HeapAdjustDown(Heap* hp,int parent)
{
int child = parent * 2 + 1;
while (child < hp->size)//保证左孩子存在
{
if (hp->array[child] > hp->array[child + 1] && child + 1< hp->size)//保证右孩子存在,要不然咋比
child = child + 1;
if (hp->array[parent] > hp->array[child])
{
Swap(&hp->array[child], &hp->array[parent]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
void HeapCreate(Heap* hp, DataType arr[], int size)//创建
{
assert(hp);
hp->array = (DataType*)malloc(sizeof(DataType)*size);
if (hp->array==NULL)//
{
assert(0);//看为什么失败
return;
}
hp->capacity = size;
memcpy(hp->array, arr, sizeof(DataType)*size);
hp->size = size;
//对堆中元素进行向下调整,重复调几次
//调上面的左右子树之前,要从倒数第一个非叶子结点开始
for (int root = (size - 2) >> 1; root >= 0;root--)
HeapAdjustDown(hp, root);
- 堆的插入与删除
堆的插入是在数组末插入元素,所以插入后,需要向上调整,把该元素放到合适的位置
删除是删数组第一个元素,也就是堆顶,需要1.堆顶与堆尾元素交换,2.向下调整元素至合适位置
void HeapPush(Heap* hp, DataType data)//插入
{
assert(hp);
CheckCapacity(hp);//检测还有无空间
hp->array[hp->size] = data;
hp->size++;
HeapAdjustUp(hp,hp->size-1);
}
void HeapPop(Heap* hp)//堆的删除
{
assert(hp);
if (HeapEmpty(hp)) return;
Swap(&hp->array[0], &hp->array[hp->size - 1]);
hp->size--;
HeapAdjustDown(hp, 0);
}
Heap.h代码
#include "Heap.h"
void Swap(int* left, int* right)
{
int tmp = *left;
*left = *right;
*right = tmp;
}
void CheckCapacity(Heap* hp)
{
assert(hp);
int newCapacity = hp->capacity * 2;
if (hp->size >= hp->capacity)
{
//需要扩容
DataType* temp = (DataType*)malloc(sizeof(DataType)*newCapacity);
if (temp == NULL)
{
assert(0);
return;
}
//memcpy(temp, hp->array, sizeof(DataType)*hp->size);
for (int i = 0; i < hp->size; i++)
temp[i] = hp->array[i];
free(hp->array);
hp->array = temp;
hp->capacity = newCapacity;
}
}
void HeapAdjustUp(Heap* hp, int child)
{
int parent = (child - 1) >> 1;
//不需要找孩子中最小的与双亲比较
//插入之前已经满足堆的特性了
while (child)
{
if (hp->array[child] < hp->array[parent])
{
Swap(&hp->array[child], &hp->array[parent]);
child = parent;
parent = (child - 1) >> 1;
}
else
return;
}
}
void HeapAdjustDown(Heap* hp,int parent)
{
int child = parent * 2 + 1;
while (child < hp->size)//保证左孩子存在
{
if (hp->array[child] > hp->array[child + 1] && child + 1< hp->size)//保证右孩子存在,要不然咋比
child = child + 1;
if (hp->array[parent] > hp->array[child])
{
Swap(&hp->array[child], &hp->array[parent]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
void HeapCreate(Heap* hp, DataType arr[], int size)//创建
{
assert(hp);
hp->array = (DataType*)malloc(sizeof(DataType)*size);
if (hp->array==NULL)//
{
assert(0);//看为什么失败
return;
}
hp->capacity = size;
memcpy(hp->array, arr, sizeof(DataType)*size);
hp->size = size;
//对堆中元素进行向下调整,重复调几次
//调上面的左右子树之前,要从倒数第一个非叶子结点开始
for (int root = (size - 2) >> 1; root >= 0;root--)
HeapAdjustDown(hp, root);
}
void HeapDestory(Heap* hp)//销毁
{
assert(hp);
if (hp->array)
{
free(hp->array);
hp->array =NULL;
hp->capacity = 0;
hp->size = 0;
}
}
void HeapPush(Heap* hp, DataType data)//插入
{
assert(hp);
CheckCapacity(hp);
hp->array[hp->size] = data;
hp->size++;
HeapAdjustUp(hp,hp->size-1);
}
void HeapPop(Heap* hp)//堆的删除
{
assert(hp);
if (HeapEmpty(hp)) return;
Swap(&hp->array[0], &hp->array[hp->size - 1]);
hp->size--;
HeapAdjustDown(hp, 0);
}
DataType HeapTop(Heap* hp)//取堆顶元素
{
assert(hp&&!HeapEmpty(hp));
return hp->array[0];
}
int HeapSize(Heap* hp)//堆的数据个数
{
assert(hp);
return hp->size;
}
int HeapEmpty(Heap* hp)//堆判空
{
assert(hp);
return hp->size == 0;
}