1.堆的概念
如果有一个关键码的集合K={k0,k1,k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:
- Ki<=K2*i+1 且 Ki<=K2*i+2,称为小堆
- Ki>=K2*i+1 且 Ki>=K2*i+2,称为大堆
(i=0,1,2…)
本质上来说,堆就是一个满足上面两个条件的完全二叉树。
2.性质
- 如果i=0,结点i是根结点,没有双亲结点;否则结点i的双亲结点为结点(i-1)/2
- 如果2*i+1<=n-1,则结点i的左孩子为结点2*i+1,否则结点无左孩子
- 如果2*i+2<=n-1,则结点i的右孩子为结点2*i+2,否则结点无右孩子
3.代码
Heap.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <malloc.h>
#define HEAPINCREMENT 10//存储空间分配增量
typedef int HPDataType;
typedef struct Heap
{
HPDataType *a;//给一个数组,由于可能给堆里添加元素,所以给成动态
int size;//当前堆中所存元素,以元素个数为单位
int capacity;//数组容量
}Heap;
void CreateHeap(Heap *hp, HPDataType *a, int size);//创建堆,此处创建的是小堆
void AdjustDown(Heap *hp, int parent);//向下调整,parent代表堆调整的初始位置(倒数第一个非叶子结点)
void AdjustUp(Heap *hp, int child);//向上调整,child代表插入元素的下标
void Swap(HPDataType *pLeft, HPDataType *pRight);//交换
void InsertHeap(Heap *hp, HPDataType data);//给堆中插入元素
void CheckCapacity(Heap *hp);//检查堆容量
void DeleteHeap(Heap *hp);//堆的删除(一般指删除堆顶元素)
int EmptyHeap(Heap *hp);//判断堆是否为空
int SizeHeap(Heap *hp);//计算堆中元素的个数
void TestHeap();
Heap.c
#include"Heap.h"
void CreateHeap(Heap * hp, HPDataType * a, int size)
{
int i = 0;
int root = (size - 1 - 1) / 2;//root:倒数第一个非叶子结点
if (hp == NULL)
{
return;
}
//给堆申请空间
hp->a = (HPDataType *)malloc(sizeof(HPDataType)*size);
if (hp->a == NULL)//如果申请空间失败
{
assert(0);//此处为什么断言?
/*
1.如果申请空间失败,程序运行到这里会停下来,调试很方便
2.让该错误不会向后蔓延,到这里程序直接停止
*/
return;
}
hp->capacity = size;
hp->size = size;
//将数组中的元素放在堆中
for (; i < size; i++)
{
hp->a[i] = a[i];
}
//堆调整
for (; root >= 0; --root)//从倒数第一个非叶子结点调整到根
{
AdjustDown(hp, root);//利用向下调整的方法调
}
}
void AdjustDown(Heap * hp, int parent)
{
int child = parent * 2 + 1;//child标记左右孩子中最小的孩子,这里先假设child是以parent为根的树的左孩子
int size = hp->size;
while (child < size)//调整终止条件:child不越界
{
//找该树中左右孩子中最小的孩子
if (hp->a[child] > hp->a[child + 1])
{
child += 1;
}
//双亲与最小孩子比较,如果大于最小孩子,则两者交换
if (child+1 < size && hp->a[parent] > hp->a[child])//如果右孩子存在且双亲大于左孩子
{
Swap(&(hp->a[parent]), &(hp->a[child]));
//交换后,大的元素下去,可能下面的树又不满足堆的定义,所以要接着调下面的树
parent = child;
child = parent * 2 + 1;
}
else//此树已经满足堆
{
return;
}
}
}
void AdjustUp(Heap * hp, int child)
{
int parent = (child - 1) / 2;//首先找到该孩子结点的双亲,然后调整
while (child)//终止条件:child不为0
{
if (hp->a[parent] > hp->a[child])
{
Swap(&(hp->a[parent]), &(hp->a[child]));
//交换后,大的元素上去,可能上面的树又不满足堆的定义,所以要接着调上面的树
child = parent;
parent = (child - 1) / 2;
}
else//此树已经满足堆
{
return;
}
}
}
void Swap(HPDataType * pLeft, HPDataType * pRight)
{
int tmp;
assert(pLeft);
assert(pRight);
tmp = *pLeft;
*pLeft = *pRight;
*pRight = tmp;
}
void InsertHeap(Heap * hp, HPDataType data)
{
int parent = 0;
int child = 0;
assert(hp);
//1.将该数插入到此树的最后一个位置,可能堆的容量不够,所以要先检查一下
CheckCapacity(hp);
//2.插入元素
hp->a[hp->size++] = data;
child = hp->size - 1;
//3.向上调整
AdjustUp(hp, hp->size - 1);
}
void CheckCapacity(Heap * hp)
{
int i = 0;
assert(hp);
if (hp->capacity == hp->size)//堆中没有空间,追加空间
{
hp->a = (HPDataType *)realloc(hp->a, (hp->capacity + HEAPINCREMENT) * sizeof(HPDataType));
if (hp->a == NULL)//追加空间失败
{
assert(0);//此处为什么断言?
/*
1.如果申请空间失败,程序运行到这里会停下来,调试很方便
2.让该错误不会向后蔓延,到这里程序直接停止
*/
return;
}
hp->capacity = hp->capacity + HEAPINCREMENT;//新空间的容量
}
}
void DeleteHeap(Heap * hp)
{
if (EmptyHeap(hp))
{
return;
}
else
{
hp->a[0] = hp->a[hp->size - 1];//1.将堆中最后一个元素与堆顶元素交换
hp->size--;//2.删除最后一个元素
AdjustUp(hp, 0);//3.堆顶元素向下调整
}
}
int EmptyHeap(Heap * hp)
{
assert(hp);
return hp->size == 0;
}
int SizeHeap(Heap * hp)
{
assert(hp);
return hp->size;
}
void TestHeap()
{
int arr[] = { 53,17,78,9,45,65,87,23,31 };
Heap hp;
CreateHeap(&hp, arr, sizeof(arr) / sizeof(arr[0]));
InsertHeap(&hp, 5);
DeleteHeap(&hp);
SizeHeap(&hp);
}