用C语言实现------堆

一,堆的概念

         如果有一个关键码的集合K = {k(0) ,k(1) ,k(2),…,k(n-1) },把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足: = 且 >= ) i = 0,1, 2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

        大堆和小堆的简单区分:

                大堆:数据从上到下逐渐减小

                小堆:数据从上到下逐渐增大

        ps:堆总是一颗完全二叉树。

二,C语言实现的思路

        1.堆的初始化

                使用结构体变量进行堆的构建

typedef int(*Func)(int left, int right); 

typedef struct Heap
{
	DataType* array;  // 1
	int capacity;     // 2
	int size;         // 3
	Func Compare;     // 4
}Heap;

        结构体变量中应当有如下成员:

                (1).定义一个指针变量,用来指向一处空间的地址。

                (2).定义变量capacity,记录空间的容量。

                (3).定义变量size,记录空间中有效元素的个数。

                (4).定义一个函数指针,由于堆的种类有大堆和小堆之分,所以在后面选择堆的种类时,使用函数指针,方便调用两个不同的函数。

void HeapCreate(Heap* hp, DataType* a, int n, PFUNC Compare)
{
    //申请空间用来保存堆中的元素
	hp->array = (DataType*)malloc(sizeof(DataType)*n);
	if (NULL == hp->array) //判断空间是否申请成功
	{
		assert(0);
		return;
	}
	hp->capacity = n;//对容量使用函数中所传递的参数n进行初始化

	memcpy(hp->array, a, sizeof(DataType)*n);//将数组a中的元素拷贝到堆(hp)中
	hp->size = n;//有效元素个数的记录
	hp->Compare = Compare;//函数指针的调用
    //从第一个非叶子节点向上进行元素的调整
	for (int root = (n - 2) / 2; root >= 0; root--)
	{
		AdjustDown(hp, root);
	}
}

                (5).使用函数完成对堆的初始化

        2.堆的插入

void HeapPush(Heap* hp, DataType x)
{
	CheckHeapCapacity(hp);

	hp->array[hp->size] = x;
	hp->size++;

	AdjustUp(hp, hp->size - 1);
}

                (1).在进行插入之前,首先要对堆中的容量进行检测,如果容量不足,则需要扩容。

                (2).将要插入的元素插入到堆中的最末尾。在用代码进行表示时,使用数组的方式,且数组的下标是size。(其中size表示堆中元素的有效个数,新插入的元素的下标就是size)最终使size++即可。

                (3).将新插入的元素进行调整。(选择使用大堆还是小堆的方式进行调整)

        3.堆的删除

                ps:默认该堆中的元素删除只能删除“节点的祖先”。

void HeapPop(Heap* hp)
{
	if (HeapEmpty(hp))//使用函数判断堆是否有元素
	{
		return;
	}
	Swap(&hp->array[0], &hp->array[hp->size - 1]);//将堆顶元素与堆中最后一个元素进行交换
	hp->size--;//将堆中有效元素个数减少1个
	AdjustDown(hp, 0);// 将堆顶元素往下调整
}

           (1).进行堆的判空。

           (2).记录堆中的元素个数。

           (3).使用函数调整插入元素后的堆的结构,使其满足大小堆的规则。

        4.获取堆顶元素的值           

DataType HeapTop(Heap* hp)
{
	assert(hp);//判空
	return hp->array[0];、//返回堆顶元素的值
}

               (1).使用断言函数assert(头文件#include<assert.h>)判断是堆是否为空 .

                 (2)   .使用数组的形式,直接返回数组下标为0的元素就是堆顶元素的值。

        5.获取堆中有限元素的个数

int HeapSize(Heap* hp)
{
	assert(hp);//判空
	return hp->size;//返回元素的个数
}

                       (1).使用断言函数assert(头文件#include<assert.h>)判断是堆是否为空 。

                       (2).返回结构体变量中的成员变量size的值即代表了堆中有限元素的个数。

        6.堆的判空

int HeapEmpty(Heap* hp)
{
	assert(hp);//判空
	return 0 == hp->size;//返回有效元素的个数
}

                (1).判空。

                (2).使用有效元素的个数来表示堆中是否为空。

        7.堆的销毁

void HeapDestory(Heap* hp)
{
	assert(hp);//判空
	free(hp->array);//释放空间
	hp->array = NULL;//使指向空间的指针指向NULL;
	hp->capacity = 0;//将容量置为0
	hp->size = 0;//将有效元素的个数置为0
}

                (1).判空。

                (2).malloc函数是在堆中申请的空间,在将空间使用完毕时,需要使用函数free手动释放。

                (3).将空间容量和有效元素的个数置为0。

三.使用到的其他辅助函数

        1.函数指针需要调用的替他函数(用来先择创建大堆还是小堆)

int Less(int left, int right)//小堆
{
	return left < right;
}
int Greater(int left, int right)//大堆
{
	return left > right;
}
//只是用来比较大小,所以不需要传递指针作为参数

        2.数据交换函数

void Swap(DataType* left, DataType* right)
{
	int temp = *left;//交换数据
	*left = *right;
	*right = temp;
}

3.向下调整函数(需要使堆在插入或者删除元素之后仍旧保持大(小)堆的性质)

void AdjustDown(Heap* hp, int parent)//从最后一个有叶子的节点逐个向上进行判断
{
	int size = hp->size;
	int* array = hp->array;
	// child默认标记parent的左孩子
	// 因为:parent如果有孩子,一定是先有左孩子再有右孩子
	int child = parent * 2 + 1;
	while (child < size)//孩子的下标一定小于有效元素的个数
	{
		//判断右孩子是否存在,并找出较小的一个
		if (child + 1 < size && hp->Compare(array[child + 1], array[child]))
			child += 1;

		// 检测parent是否满足堆的特性
		if (hp->Compare(array[child], array[parent]))
		{
			Swap(&array[child], &array[parent]);
			// 需要继续往下检测
			parent = child;
			child = parent * 2 + 1;
		}
		else
		    return;
	}
}

4.向上调整函数

void AdjustUp(Heap* hp, int child) 
{
	int* array = hp->array;
	//变换双亲和孩子之间的位置关系
	int parent = (child - 1) / 2;//逐个向上进行调整
	while (child)//不为0
	{
		if (hp->Compare(array[child], array[parent]))
		{
			Swap(&array[child], &array[parent]);
			child = parent;
			parent = (child - 1) / 2;//性质公式
		}
		else
			return;
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值