堆是一种数据结构,他的特点在于形成某种优先的结构。在计算机经常用到,比如优先队列,或者是优先进程管理。
堆(也叫二叉堆)的性质:
1、任何一个节点,都不大于他的父亲节点。
2、必须是一颗完全二叉树
比如下面:
我们用一个很经典的堆的实现方法:用数组实现堆。
注意,这里实现的叫最大堆,就是把最大元素放在顶端,最小堆的实现方法相同。
如图,我们给一个堆的每个元素编号:
这样的好处在于,对于每个节点来说,他的左孩子的编号,是父亲节点的2倍,这也是用数组实现的基础,于是有了这3个公式:
- 父节点编号 = 当前节点 ➗ 2
- 左孩子节点 = 当前节点 × 2
- 右孩子节点 = 当前节点 × 2 + 1
------------------------------------分割线-----------------------------------------------
以上是堆的性质。下面是如何构造一个堆:
- 堆的构造:数组,count表示内容大小,maxSize表示最大容量。
- 堆的判空、返回大小、初始化都很简单,直接返回性质(具体看最后代码)。
- 入堆:入堆需要判断他的大小,方法是:先将他放在最后面的位置(如图),然后依次和他的父亲比较,只要比父亲大,就交换。
- 出堆:直接取走顶端元素(arr[1]),然后把最后的元素挪到最前面,然后把它进行下移的操作。最后把数组最后一个元素删掉,并且count - 1就完成了。
下移操作详解:①取出左右孩子中最大值。②把最大值和当前节点比较,如果小于最大值,就交换(这样确保换上去的在3个节点里是最大的)。③吧当前点设置为换到的那个位置,继续往下循环,直到没有孩子,或者孩子都比他小。
以上是其中一种堆的实现方法,还有一种效率比较高的方法,就是在初始化堆的时候,直接传入数组,从而直接初始化成一个堆:先构造成一颗树,然后从第一个不是叶子节点的点开始(size / 2)到1号位置,做下移操作。大家可以试一试,在后面代码里有,也在别的博客里写到了:C语言实现heapify堆转换算法。
代码:
头文件:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int HeapDataType;
typedef struct MaxHeap{
HeapDataType* data;
int count;
int MaxSize;
}MH;
//-----------堆的构建等等方法
int size(MH *mh);//返回堆大小
int isEmpty(MH *mh);//判空
void initMaxHeap(MH* mh, int size);//初始化堆
void initMaxHeap2(MH* mh, int size, HeapDataType* arr);//第二种初始化堆,heapify算法
void AdjustUp(MH* mh, int k);//上移元素
void AdjustDown(MH* mh, int k);//下移操作
void insertMaxHeap(MH* mh, HeapDataType value);//插入元素
HeapDataType TopK(MH* mh);//弹出元素
void TestMaxHeap();//测试函数
堆:
#include "MH.h"
//返回堆大小
int size(MH *mh){
return mh->count;
}
//判空
int isEmpty(MH *mh){
return 0 == mh->count;
}
//下移
void AdjustDown(MH* mh, int k){
while (k * 2 <= mh->count)
{
int j = k * 2;
if (j + 1 <= mh->count && mh->data[j] < mh->data[j + 1])//如果右孩子存在且右孩子比左孩子大
{
j = j + 1;
}
if (mh->data[k] > mh->data[j])//如果节点比孩子大
{
break;
}
//否则交换k和j
int tmp = mh->data[k];
mh->data[k] = mh->data[j];
mh->data[j] = tmp;
k = j;
}
}
//初始化堆
void initMaxHeap(MH* mh, int size){
mh->MaxSize = size;
mh->data = (HeapDataType*)malloc((mh->MaxSize + 1) * sizeof(HeapDataType));//从1开始存储
mh->count = 0;
}
//第二种初始化堆,heapify算法
void initMaxHeap2(MH* mh, int size, HeapDataType* arr){
mh->MaxSize = size;
mh->data = (HeapDataType*)malloc((mh->MaxSize + 1) * sizeof(HeapDataType));//从1开始存储
//吧arr数组的值赋给这个堆
for (int i = 0; i < size; i++)
{
mh->data[i + 1] = arr[i];
}
mh->count = size;
//整合堆操作
for (int i = mh->count / 2; i > 0; i--)
{
AdjustDown(mh, i);
}
}
//上移元素
void AdjustUp(MH* mh, int k){
while (1 < k && mh->data[k / 2] < mh->data[k])
{
int tmp = mh->data[k / 2];
mh->data[k / 2] = mh->data[k];
mh->data[k] = tmp;
k /= 2;
}
}
//插入元素
void insertMaxHeap(MH* mh, HeapDataType value){
//看看有没有满
assert(mh->count + 1 <= mh->MaxSize);
//count为最后一个元素
mh->data[mh->count + 1] = value;
mh->count++;
AdjustUp(mh,mh->count);//上移到合适位置
}
//弹出元素
HeapDataType TopK(MH* mh){
assert(mh->count > 0);
//获得顶端元素
HeapDataType res = mh->data[1];
//把最后的赋值给顶端
mh->data[1] = mh->data[mh->count];
//最后的元素置0,并且count--
mh->data[(mh->count)--] = 0;
//把顶端元素下移到合适位置
AdjustDown(mh, 1);
return res;
}
void TestMaxHeap()
{
MH mh;
initMaxHeap(&mh, 10);
insertMaxHeap(&mh, 2);
insertMaxHeap(&mh, 1);
insertMaxHeap(&mh, 3);
insertMaxHeap(&mh, 5);
insertMaxHeap(&mh, 4);
for (int i = 0; i < 5; i++)
{
int x = TopK(&mh);
printf("%d ", x);
}
system("pause");
return 0;
}
关于堆排序,请参照:堆排序——C语言实现