一、堆的概念及结构
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
- 堆中某个节点的值总是不大于或不小于其父节点的值;
- 堆总是一棵完全二叉树。
- 堆顶元素一定是最大的或最小的


二、堆的实现
1、堆向下调整算法
现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。
int a[] = {27,15,19,18,28,34,65,49,25,37};
//思路
//1、找parent较小的孩子
int child=parent*2+1;//parent的左孩子
while(child<size){
//保证右孩子存在:child+1<size
if(child+1<size&&array[child+1]<array[child])
child+=1;
//检测parent是否满足堆得性质
if(array[child]<array[parent])
Swap(&array[parent],&array[child]);
//双亲比孩子大,交换完成后,较大的双亲往下移动,可能会导致字数不满足堆的性质
parent=child;
child=parent*2+1;
else
return;
}

2、堆的创建
下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。
int a[] = {1,5,3,8,7,6};

3、堆的插入
先插入一个80到数组的尾上,再进行向上调整算法,直到满足堆。

4、 堆的删除
删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

5 、堆排序

三、堆得代码实现
typedef int HDataType;
int Less(HDataType left, HDataType right);
int Greater(HDataType left, HDataType right);
//函数指针变量:可以指向任何有两个HDataType参数以及一个int类型返回值得所有函数
//cmp势函数指针变量,Cmp可以指向Less,也可以指向Greater
//Cmp的类型:int(*)(HDataType,HDataType)
static int (*Cmp)(HDataType left, HDataType right);
typedef int(*CMP)(HDataType, HDataType);
typedef struct Heap
{
HDataType* array;
int capacity;
int size;
CMP Cmp;//函数指针变量
}Heap;
int Less(HDataType left, HDataType right)
{
return left < right;
}
int Greater(HDataType left, HDataType right)
{
return left > right;
}
//初始化一个空堆
void HeapInit(Heap* hp, int initCap, CMP cmp)
{
assert(hp);
initCap = initCap < 10 ? 10 : initCap;
hp->array = (HDataType*)malloc(sizeof(HDataType) * initCap);
if (NULL == hp->array)
{
assert(0);
return;
}
hp->capacity = initCap;
hp->size = 0;
//接受比较方式
hp->Cmp = cmp;
}
void Swap(HDataType* left, HDataType* right)
{
HDataType temp = *left;
*left = *right;
*right = temp;
}
void AdjustDown(Heap* hp, int parent)
{
//用child标记娇小的孩子
int size = hp->size;
int* array = hp->array;
int child = parent * 2 + 1;
while (child < size)
{
//找到parent中两个孩子中娇小的孩子
if (child + 1 < size && hp->Cmp(array[child + 1],array[child]))//array[child + 1] > array[child]大堆
{
child += 1;
}
//parent娇小的孩子已经找到
//检测parent是否满足堆得性质
if (hp->Cmp(array[child] , array[parent]))//array[child] > array[parent]
{
Swap(&array[child], &array[parent]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
//用数组中的元素创建堆
void HeapCreate(Heap* hp, int* array, int size, CMP cmp)
{
//1、先将数组的元素放入堆中
HeapInit(hp, size, cmp);
memcpy(hp->array, array, sizeof(HDataType*) * size);
hp->size = size;
//2、进行调整
for (int root = (size - 2) / 2; root >= 0; root--)
{
AdjustDown(hp, root);
}
}
void AdjustUP(Heap* hp, int child)
{
HDataType* array = hp->array;
int size = hp->size;
int parent = ((child - 1) >> 1);
while (child)
{
if (hp->Cmp(array[child] ,array[parent]))
{
Swap(&array[child], &array[parent]);
child = parent;
parent = ((child - 1) >> 1);
}
else
{
return;
}
}
}
void CheckCapacityHP(Heap* hp)
{
assert(hp);
if (hp->size == hp->capacity)
{
int newCapacity = hp->capacity * 2;
HDataType* temp = (HDataType*)malloc(sizeof(HDataType) * newCapacity);
if (temp == NULL)
{
assert(0);
return;
}
//memcpy(temp, hp->array, hp->size * sizeof(HDataType));
for (int i = 0; i < hp->size; ++i)
temp[i] = hp->array[i];
free(hp->array);
hp->array = temp;
hp->capacity = newCapacity;
}
}
void HeapPush(Heap* hp, HDataType data)
{
assert(hp);
//0、检测是否需要扩容
CheckCapacityHP(hp);
//1、将新元素插入数组的末尾,将元素插入到完全二叉树最后
hp->array[hp->size++] = data;
//2、新元素插入后,可能会破坏堆得性质
AdjustUP(hp, hp->size - 1);
}
void HeapPop(Heap* hp)
{
if (HeapEmpty(hp))
return;
Swap(&hp->array[0], &hp->array[hp->size - 1]);
hp->size--;
AdjustDown(hp,0);
}
//获取堆顶元素
HDataType HeapTop(Heap* hp)
{
assert(!HeapEmpty(hp));
return hp->array[0];
}
//堆为空返回非0值
int HeapEmpty(Heap* hp)
{
assert(hp);
return 0 == hp->size;
}
int HeapSize(Heap* hp)
{
assert(hp);
return hp->size;
}
void HeapDestroy(Heap* hp)
{
assert(hp);
free(hp->array);
hp->array = NULL;
hp->capacity = 0;
hp->size = 0;
}
void TestHeap()
{
int array[] = { 3,6,9,1,5,2,0,7,8,4 };
Heap hp;
HeapCreate(&hp, array, sizeof(array) / sizeof(array[0]),Less);
printf("heap size=%d\n", HeapSize(&hp));
printf("heap top=%d\n", HeapTop(&hp));
HeapPop(&hp);
printf("heap size=%d\n", HeapSize(&hp));
printf("heap top=%d\n", HeapTop(&hp));
HeapPush(&hp, 0);
HeapDestroy(&hp);
}
本文介绍了堆的概念,包括小堆和大堆的定义,以及堆的性质。详细阐述了堆的实现过程,包括向下调整、创建堆、插入元素、删除元素等操作。同时提到了堆在堆排序中的应用。
588





