使用说明
本文的堆以小根堆为例,给出模板
使用时将ElementType 修改为对应的数据类型就好
1.插入式建堆
建一个空堆,将元素一个一个插入,调整
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef struct HeapStruct {
ElementType *Element;
int Size;
int MaxSize;
} Heap, *MinHeap;
// 方法一: 创建一个大小为 MaxSize 的堆
MinHeap Create_Heap(int MaxSize) {
MinHeap heap = (MinHeap *) malloc (sizeof(Heap)); // 申请一个堆
heap->Element = (ElementType *) malloc (MaxSize+1)*sizeof(ElementType); // 为堆申请存储元素的空间大小,即堆的能够存储的结点数
heap->Size = 0; // 堆的当前元素数
heap->MaxSize = MaxSize; // 堆的最大存储空间
heap->Element[0] = INT_MIN; // 将指针数组的 0 号位置置为无穷小,堆顶从 1 号开始
}
// 使用后释放堆申请的空间
void Destroy(MinHeap heap) {
free(heap->Element); // 先释放堆结点的数组空间
free(heap); // 再释放堆结点的空间
}
// 判断堆是否为空
bool IsEmpty(MinHeap heap) {
return (heap->Size == 0);
}
// 判断堆是否满了
bool IsFull(MinHeap heap, ElementType Size) {
return (heap->Size == heap->MaxSize);
}
// 向堆中插入元素
void Insert(MinHeap heap, ElementType item) {
int i = 0;
// 检查是否有空间可以插入元素
if(IsFull(heap)) {
printf("Heap is full!\n");
return ;
}
/*
将item加入为堆的一个叶子结点,然后向上调整至合适的位置插入,到堆顶结束
*/
heap->Size ++; // 堆中的元素数量增加1
for(i = heap->Size; heap->Element[i / 2] > item; i = i / 2) { // 向上与父节点做比较
heap->Element[i] = heap->Element[i / 2]; // 父节点下移
}
heap->Element[i] = item;
}
// pop并返回堆顶
ElementType Pop(MinHeap heap) {
ElementType item, temp; // 取出堆顶
int parent = 0, child = 0;
if(IsEmpty(heap)) {
printf("Heap is Empty!\n");
return heap->Element[0]; // 相当于一种安全保护
}
/*
如果堆不空,则将堆顶取出作为返回值,
将最后一个结点也就是二叉堆的一个叶子结点放在堆顶。
将堆顶向下调整至合适位置后停止
*/
item = heap->Element[1];
temp = heap->Element[heap->Size];
heap->Size --; // 堆中元素减少一个
for(int parent = 1; parent * 2 <= heao->Size; parent = child) {
child = parent * 2;
// 找出左右子节点中较小的那个
if(child != heap->Size && (heap->Element[child] > heap->Element[child + 1])) {
child++;
}
// 比较父节点与子节点的大小,如果父节点大就,则二者互换位置继续寻找合适的插入位置,否则?
if(temp > heap->Element[child]) {
heap->Element[parent] = heap->Element[child];
}
else break;
}
heap->Element[child] = temp;
return item;
}
int main() {
int x, n;
scanf("%d", &n);
MinHeap H = Create(n);
for(int i = 1; i <= n; i++) {
scanf("%d", &x);
Insert(H, x);
}
return 0;
}
2.直接在数组基础上调整排序建堆
直接将存数据的数组建堆,从每颗小的子树开始调整,
// 方法二: 已知一个数组,创建一个由数组组成的最小堆
MinHeap BuildMinHeap(ElementType *Element, int Size, int MaxSize) {
MinHeap H = Create(MaxSize); // 创建一个空的
ElementType temp;
int parent = 0, child = 0;
// 判断存储空间够不够用
if (Size > H->MaxSize) {
printf("最小堆的存储空间不够用\n");
return NULL;
}
// 将数组中的元素先全部复制到堆里
for(int i = 0; i < Size; i++) {
H->Element[i+1] = Element[i];
}
H->Size = Size;
// 给最小堆元素排序
/*
从最后一个节点的父节点开始,自右向左,自下向上将每个父节点与其两个子节点做比较后上调
直到堆顶与其左右子节点做比较 ,然后将堆顶下沉
该建堆方法比一个一个插入更优,因为不需要将叶子结点从堆顶移动到叶子位置(当然最坏情况是一样的)
*/
for(parent = H->Size / 2; parent >= 1; parent--) {
temp = H->Element[parent];
for(; parent * 2 <= H->Size; parent = child) {
child = parent * 2;
// 如果左右子树都存在,将父节点与其中较小的比较后选择是否下沉
if(child != H->Size && (H->Element[child] > H->Element[child + 1])) {
child ++;
}
// 选择父节点是否下沉
if(temp > H->Element[child]) {
H->Element[parent] = H->Element[child];
}
else break;
}
H->Element[parent] = temp;
}
return H;
}
完整代码
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef struct HeapStruct {
ElementType *Element;
int Size;
int MaxSize;
} Heap, *MinHeap;
// 方法一: 创建一个大小为 MaxSize 的堆
MinHeap Create_Heap(int MaxSize) {
MinHeap heap = (MinHeap *) malloc (sizeof(Heap)); // 申请一个堆
heap->Element = (ElementType *) malloc (MaxSize+1)*sizeof(ElementType); // 为堆申请存储元素的空间大小,即堆的能够存储的结点数
heap->Size = 0; // 堆的当前元素数
heap->MaxSize = MaxSize; // 堆的最大存储空间
heap->Element[0] = INT_MIN; // 将指针数组的 0 号位置置为无穷小,堆顶从 1 号开始
}
// 方法二: 已知一个数组,创建一个由数组组成的最小堆
MinHeap BuildMinHeap(ElementType *Element, int Size, int MaxSize) {
MinHeap H = Create(MaxSize); // 创建一个空的
ElementType temp;
int parent = 0, child = 0;
// 判断存储空间够不够用
if (Size > H->MaxSize) {
printf("最小堆的存储空间不够用\n");
return NULL;
}
// 将数组中的元素先全部复制到堆里
for(int i = 0; i < Size; i++) {
H->Element[i+1] = Element[i];
}
H->Size = Size;
// 给最小堆元素排序
/*
从最后一个节点的父节点开始,自右向左,自下向上将每个父节点与其两个子节点做比较后上调
直到堆顶与其左右子节点做比较 ,然后将堆顶下沉
该建堆方法比一个一个插入更优,因为不需要将叶子结点从堆顶移动到叶子位置(当然最坏情况是一样的)
*/
for(parent = H->Size / 2; parent >= 1; parent--) {
temp = H->Element[parent];
for(; parent * 2 <= H->Size; parent = child) {
child = parent * 2;
// 如果左右子树都存在,将父节点与其中较小的比较后选择是否下沉
if(child != H->Size && (H->Element[child] > H->Element[child + 1])) {
child ++;
}
// 选择父节点是否下沉
if(temp > H->Element[child]) {
H->Element[parent] = H->Element[child];
}
else break;
}
H->Element[parent] = temp;
}
return H;
}
// 使用后释放堆申请的空间
void Destroy(MinHeap heap) {
free(heap->Element); // 先释放堆结点的数组空间
free(heap); // 再释放堆结点的空间
}
// 判断堆是否为空
bool IsEmpty(MinHeap heap) {
return (heap->Size == 0);
}
// 判断堆是否满了
bool IsFull(MinHeap heap, ElementType Size) {
return (heap->Size == heap->MaxSize);
}
// 向堆中插入元素
void Insert(MinHeap heap, ElementType item) {
int i = 0;
// 检查是否有空间可以插入元素
if(IsFull(heap)) {
printf("Heap is full!\n");
return ;
}
/*
将item加入为堆的一个叶子结点,然后向上调整至合适的位置插入,到堆顶结束
*/
heap->Size ++; // 堆中的元素数量增加1
for(i = heap->Size; heap->Element[i / 2] > item; i = i / 2) { // 向上与父节点做比较
heap->Element[i] = heap->Element[i / 2]; // 父节点下移
}
heap->Element[i] = item;
}
// pop并返回堆顶
ElementType Pop(MinHeap heap) {
ElementType item, temp; // 取出堆顶
int parent = 0, child = 0;
if(IsEmpty(heap)) {
printf("Heap is Empty!\n");
return heap->Element[0]; // 相当于一种安全保护
}
/*
如果堆不空,则将堆顶取出作为返回值,
将最后一个结点也就是二叉堆的一个叶子结点放在堆顶。
将堆顶向下调整至合适位置后停止
*/
item = heap->Element[1];
temp = heap->Element[heap->Size];
heap->Size --; // 堆中元素减少一个
for(int parent = 1; parent * 2 <= heao->Size; parent = child) {
child = parent * 2;
// 找出左右子节点中较小的那个
if(child != heap->Size && (heap->Element[child] > heap->Element[child + 1])) {
child++;
}
// 比较父节点与子节点的大小,如果父节点大就,则二者互换位置继续寻找合适的插入位置,否则?
if(temp > heap->Element[child]) {
heap->Element[parent] = heap->Element[child];
}
else break;
}
heap->Element[child] = temp;
return item;
}