堆——C语言实现

堆是一种数据结构,他的特点在于形成某种优先的结构。在计算机经常用到,比如优先队列,或者是优先进程管理。

堆(也叫二叉堆)的性质:

1、任何一个节点,都不大于他的父亲节点。

2、必须是一颗完全二叉树

比如下面:

堆——C语言实现_第1张图片

我们用一个很经典的堆的实现方法:用数组实现堆。

注意,这里实现的叫最大堆,就是把最大元素放在顶端,最小堆的实现方法相同。

如图,我们给一个堆的每个元素编号:

堆——C语言实现_第2张图片

 

这样的好处在于,对于每个节点来说,他的左孩子的编号,是父亲节点的2倍,这也是用数组实现的基础,于是有了这3个公式:

  1. 父节点编号 = 当前节点 ➗ 2
  2. 左孩子节点 = 当前节点 × 2
  3. 右孩子节点 = 当前节点 × 2 + 1

------------------------------------分割线-----------------------------------------------

以上是堆的性质。下面是如何构造一个堆:

 堆——C语言实现_第3张图片

  1. 堆的构造:数组,count表示内容大小,maxSize表示最大容量。
  2. 堆的判空、返回大小、初始化都很简单,直接返回性质(具体看最后代码)。
  3. 入堆:入堆需要判断他的大小,方法是:先将他放在最后面的位置(如图),然后依次和他的父亲比较,只要比父亲大,就交换。
  4. 出堆:直接取走顶端元素(arr[1]),然后把最后的元素挪到最前面,然后把它进行下移的操作。最后把数组最后一个元素删掉,并且count - 1就完成了。

下移操作详解:①取出左右孩子中最大值。②把最大值和当前节点比较,如果小于最大值,就交换(这样确保换上去的在3个节点里是最大的)。③吧当前点设置为换到的那个位置,继续往下循环,直到没有孩子,或者孩子都比他小。

堆——C语言实现_第4张图片

以上是其中一种堆的实现方法,还有一种效率比较高的方法,就是在初始化堆的时候,直接传入数组,从而直接初始化成一个堆:先构造成一颗树,然后从第一个不是叶子节点的点开始(size / 2)到1号位置,做下移操作。大家可以试一试,在后面代码里有,也在别的博客里写到了:C语言实现heapify堆转换算法。

 

代码:

头文件:

#define _CRT_SECURE_NO_WARNINGS 1

#include 
#include 
#include 

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语言实现

你可能感兴趣的:(c语言学习)