堆的概念:给一个含n项数的集合,把它所有的元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足每个节点的父亲节点大于等于(或小于等于)自己,称为大堆(或小堆)。
图示:
当堆Push数据(堆尾插数据)时,为了维持堆,需要和父节点比较,大小顺序不一致就需要调整,调整一次后还要再跟父节点比较,顺序不一致继续调,此过程封装一个Adjustup函数。
拿上图的小堆举个例子,如果push的值是15,15和它的父节点30比较之后,为了保证是小堆,15需要和30互换位置,但是还没完,需要继续往上与父节点比较,判断满不满足小堆,不满足继续和父节点互换位置。
Adjustup函数(小堆)代码实现如下:
Swap(HPDataType* a, HPDataType* b)//交换
{
HPDataType tmp =*a;
*a = *b;
*b = tmp;
}
void Adjustup(HPDataType* a, int child)//向上调整
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (a[child] < a[parent])// < 代表小堆
{
Swap(&a[child],&a[parent]);
child = parent;//重复
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
当堆Pop数据(出堆顶数据)时,为了不影响树的结构,此时引入一个方法:将堆顶和堆底数据互换,删除堆底数据,但此时堆顶的数据是维持不了堆的,需要调整至下一层,也有可能这一层也维持不了堆,还需要继续向下调整,直至最底。调整的过程封装一个Adjustdown函数。
Adjustdown函数代码实现如下:
void Adjustdown(HPDataType* a, int n, int parent)//向下调整
{
int child = parent * 2 + 1;
while (child<n)
{
if ((a[child+1] < a[child]) // < 代表小堆
&& (child + 1<n))//另一个孩子存在
child++; //只有一个孩子不用管
if (a[child] < a[parent])// < 代表小堆
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
既然堆用数组实现,不妨构建一个结构体,包含一个指针(指向数组)、数组个数size、数组的容量(动态开辟空间)。
堆的实现包含堆的初始化、销毁、堆的插入、堆的删除、取堆的个数、取堆顶数据、堆的判空。
预告:下一节介绍用数组构建堆的两种方法及其时间复杂度。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<time.h>
typedef int HPDataType;
typedef struct Heap
{
HPDataType* _a;
int _size;
int _capacity;
}Heap;
void HeapCreate(Heap* hp, HPDataType* a, int n);// 用数组构建堆
void HeapInit(Heap* hp);//堆的初始化
void HeapDestory(Heap* hp);// 堆的销毁
void Adjustup(HPDataType* a, int child);//向上调整
void HeapPush(Heap* hp, HPDataType x);// 堆的插入
void Adjustdown(HPDataType* a, int n, int parent);//向下调整
void HeapPop(Heap* hp);// 堆的删除
HPDataType HeapTop(Heap* hp);// 取堆顶的数据
int HeapSize(Heap* hp);// 堆的数据个数
int HeapEmpty(Heap* hp);// 堆的判空,空返回1,非空返回0
void HeapDestory(Heap* hp)// 堆的销毁
{
assert(hp);
free(hp->_a);
hp->_a = NULL;
hp->_capacity = hp->_size = 0;
}
void HeapInit(Heap* hp)//堆的初始化
{
assert(hp);
hp->_a = NULL;
hp->_capacity = hp->_size = 0;
}
void HeapPush(Heap* hp, HPDataType x)// 堆的插入
{
assert(hp);
int newcapacity = hp->_capacity == 0 ? 4: hp->_capacity * 2;
Heap* tmp = (Heap*)realloc(hp->_a,sizeof(HPDataType)*newcapacity);
if (tmp == NULL)
{
perror("realloc failed");
exit(-1);
}
hp->_a = tmp;
hp->_a[hp->_size] = x;
hp->_size++;
Adjustup(hp->_a, hp->_size-1);//调整顺序,保持堆
}
void HeapPop(Heap* hp)// 堆的删除
{
assert(hp);
assert(hp->_size>0);
swap(&hp->_a[0], &hp->_a[hp->_size - 1]);
hp->_size--;
Adjustdown(hp->_a, hp->_size,0);//向下调整
}
HPDataType HeapTop(Heap* hp)// 取堆顶的数据
{
assert(hp);
assert(hp->_size > 0);
return hp->_a[0];
}
int HeapSize(Heap* hp)// 堆的数据个数
{
assert(hp);
return hp->_size;
}
int HeapEmpty(Heap* hp)// 堆的判空,空返回1,非空返回0
{
assert(hp);
if (hp->_size==0)
return 1;
else
return 0;
}