【C语言】-- 数据结构 -- 堆的实现(小堆)

目录

1. 堆的概念及结构

2. 堆的基本功能实现

    2.1 初始化堆 

    2.2 数据交换 

    2.3 大小堆的调整

  2.3.1 向上调整(小根堆) -- 多数利用于存入数据

  2.3.2 向下调整(小根堆) -- 多数利用于删除数据

    2.4 存入数据 

    2.5 打印数据 

    2.6 所含数据的多少

    2.7 删除数据(堆顶元素)

    2.8 堆顶元素

    2.9 销毁堆

    2.10 判断是否为空


1. 堆的概念及结构

        按完全二叉树的顺序存储方式存储 在一个一维数组中,将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值。
  • 堆总是一棵完全二叉树。

堆的调整算法有多种,其中典型的是:

  • 小堆:父亲位,比孩子位,要小。
  • 大堆:父亲位,比孩子位,要大。

2.堆的基本功能实现

typedef int HeapDataType;	//便于更改堆所存的数据的类型
typedef struct Heap
{
	HeapDataType* a;	//利用动态开辟所创建的一维数组
	int size;	//数据个数
	int capacity;	//空间大小
}HP;

//初始化堆
void HeapInit(HP* ph);
//向上调整(小根堆)
void AdjustUp(HeapDataType* a, int child);
//向下调整(小根堆)
void AdjustDwon(HeapDataType* a, int size, int parent);
//存入数据
void HeapPush(HP* ph, HeapDataType n);
//打印数据
void HeapPrint(HP* ph);
//删除数据(堆顶元素)
void HeapPop(HP* ph);
//所含数据的多少
int HeapSize(HP* ph);
//堆顶元素
HeapDataType HeapTop(HP* ph);
//销毁堆
void HeapDestroy(HP* ph);
//堆是否为空
//为空返回true,非空返回false
bool HeapEmpty(HP* ph);

    2.1 初始化堆 

//初始化堆
void HeapInit(HP* ph)
{
	assert(ph);    //防止野指针

    //初始化
	ph->a = NULL;
	ph->capacity = ph->size = 0;
}

    2.2 数据交换 

//数据交换
void Swap(HeapDataType* e1, HeapDataType* e2)
{
	int tmp = *e1;
	*e1 = *e2;
	*e2 = tmp;
}

    2.3 大小堆的调整

下标计算父子间的关系:

  • leftchild = parent * 2 + 1
  • rightchild = parent * 2 + 2
  • parent = (child - 1) / 2 (因为只会保留整数部分)

        2.3.1 向上调整(小根堆) -- 多数利用于存入数据

  1. 如果比父亲小,则交换,然后继续向上比较并调整(最多调整到跟节点就结束了)。
  2. 如果比父亲大,则调整结束。

(父节点与子节点大小的比较,子对父)

//向上调整(小根堆)
void AdjustUp(HeapDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{ 
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}

}

        2.3.2 向下调整(小根堆) -- 多数利用于删除数据

        选出左右孩子中小的那一个。

以小的这个孩子跟父亲比较:

  • 如果比父亲小,则交换,然后继续向下比较并调整。(最多调整到叶子节点就结束了)
  • 如果比父亲大,则调整结束。

(父节点与子节点大小的比较,父对子)

//向下调整(小根堆)
void AdjustDwon(HeapDataType* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child + 1] < a[child])    //在保证两个孩子都存在的同时,挑出最小的
		{
			++child;
		}

		if (a[child] < a[parent])    //孩子小于父亲即符合
		{
			Swap(&a[child], &a[parent]);     //交换元素
			parent = child;    
			child = parent * 2 + 1;    //更改孩子的定义将原孩子变为下一个孩子
		}
		else
		{
			break;    //孩子大于父亲,就没必要进行交换了。
		}
	}

}

    2.4 存入数据 

//存入数据
void HeapPush(HP* ph, HeapDataType n)
{
	assert(ph);

	if (ph->size == ph->capacity)
	{
		ph->capacity = ph->capacity == 0 ? 4 : ph->capacity * 2;    //扩容所扩的大小	                
        HeapDataType* tmp = (HeapDataType*)realloc(ph->a, sizeof(HeapDataType) * ph->capacity);

        //防止realloc扩容失败
		if (NULL == tmp)
		{
			printf("realloc fail\n");
			exit(-1);
		}
        //扩容成功,放入结构体变量中
		ph->a = tmp;
	}

    //将数据存入
	ph->a[ph->size] = n;

    //数据已存入,个数即+1
	ph->size++;

    //向上调整(小根堆)
	AdjustUp(ph->a, ph->size - 1);
}

    2.5 打印数据 

//打印数据
void HeapPrint(HP* ph)
{
	assert(ph);

	int i = 0;
	for (i = 0; i < ph->size; i++)
	{
		printf("%d ", ph->a[i]);
	}
	printf("\n");
}

    2.6 所含数据的多少

//所含数据的多少
int HeapSize(HP* ph)
{
	assert(ph);
	
	return ph->size;
}

    2.7 删除数据(堆顶元素)

        这里需要考虑到删除头数据的问题,(不用考虑其余位置删除的问题,因为根本没有意义,因为只有顶元素代表最大值(大根堆)或最小值(小根堆))。

        而在删除数据中,如果我们运用平时的删除习惯的话:

        而我们如果不移动顺序的话,我们可以发现:

        如此,利用向下调整,有利于降低时间复杂度。 

//删除数据(堆顶元素)
void HeapPop(HP* ph)
{
	assert(ph);
	assert(ph->size > 0);
	
	ph->a[0] = ph->a[ph->size - 1];
	ph->size--;
    
    //向下调整(小根堆)
	AdjustDwon(ph->a, ph->size, 0);
}

    2.8  堆顶元素

//堆顶元素
HeapDataType HeapTop(HP* ph)
{
	assert(ph);
	assert(ph->size > 0);

	return ph->a[0];
}

    2.9 销毁堆

        堆此处是利用动态内存开辟的,所以以free归还,是必要的。

//销毁堆
void HeapDestroy(HP* ph)
{
	assert(ph);

	free(ph->a);
	ph->a = NULL;
	ph->capacity = ph->size = 0;
}

    2.10 判断是否为空

//堆是否为空
//为空返回true,非空返回false
bool HeapEmpty(HP* ph)
{
	assert(ph);

	return ph->size == 0;
}
  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

川入

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值