堆的实现(c语言版)

背景

堆c语言代码实现,方便以后复习

1、堆的性质

1、堆的逻辑结构是一棵完全二叉树,堆的物理结构是数组(顺序表)
2、大堆:树中任意一个父节点的值>=其对应孩子的值
    小堆:树中任意一个父节点的值<=其对应孩子的值

由于堆的逻辑结构是完全二叉树/满二叉树,在其底层存储时用一个数组顺序表)存储,存储数据时对应的父亲和孩子的索引值有这样的一个规律

leftchild = parent * 2 -1
rightchild = parent * 2 -2
parent = (child - 1)/2

在这里插入图片描述

2、堆中插入(push)数据分析

**堆中插入数据时需明确
1、在哪里插入?          2、插入时要满足堆的性质 (大堆/小堆)
在堆中插入数据时,先尾插数据,然后判断尾插的数据是否满足堆的性质,如果满足则直接尾插,不满足堆的性质,则需要向上调整。直到满足堆性质即可

*Notice:
*1、往堆中插入数据只会影响其祖先,不会影响其兄弟
2、向上调整的前提是前面的数据是堆

堆中插入数据流程如下图所示
在这里插入图片描述
这里还有一个问题就是向上调整的过程什么时候终止呢?
这里有两种方式
1、child所对应的节点为根节点时即 (child ==0)
2、parent<0时。
Notice:
这里其实对于parent<0是一个陷阱,parent不可能小于0,
最极端情况是等于0比如当child == 0时
parent = (child -1) /2 = 0,所有最极端情况也是等于0

这里一般终止条件采用child == 0

code

//向上调整
void AdjustUp(HPDataType* 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;
		}
	}
}

//堆中插入数据
void HeapPush(HP* php, int child)
{
	assert(php != NULL);
	if (php->size == php->capacity)  //如果容量已满,扩容
	{
		//扩容
		int newCapacity = ((php->capacity == 0) ? 4 : sizeof(php->a) * 2);
		HPDataType* ptr = (HPDataType*)realloc(php->a, sizeof(HPDataType)*newCapacity);
		if (ptr == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = ptr;
		php->capacity = newCapacity;
	}

	php->a[php->size] = child;   //插入数据
	php->size++;
	AdjustUp(php->a, php->size -1);     //向上调整是对已有的数据通过其索引来找到对应的值进行调整,所有只需要传入php->a以及尾插数据的索引值
}

3、堆中删除(pop)数据分析

pop堆中的数据要明确
1、pop哪里的数据?
pop堆中数据时,pop的默认就是根节点(也就是数组下标为0时所对应的元索),而不是最后一个叶子节点。要是最后一个叶子节点也不需要你pop呀,直接–size不就好了嘛
2、如何pop这个数据?
pop根节点时有两种方式
(1)直接将根节点之后的元素前移覆盖根节点

在这里插入图片描述
**如果直接采用挪动覆盖第一个根节点位置的方法,则此时pop之后剩余的元素关系全乱了(兄弟变父子),剩余的值也不定是一个堆(特殊情况下可能是堆)
所以直接挪动覆盖第一个根节点位置的方法不可取。

(2)根节点和最后一个叶子节点值进行交换,然后在删除最后一个叶子节点,最后向下调整直到满足堆的性质为止(其实就是相当于删除了根节点)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

code

//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)       //由于这里的n为元素的个数所以child最大为n-1 
	{
		if ((child +1 < n)&&(a[child] > a[child + 1]))  //如果大于则左孩子变右孩子
		{
			++child;
		}

		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}	
}

//pop堆中数据
void HeapPop(HP* php)
{
	assert(php != NULL);

	Swap(&(php->a[0]), &(php->a[php->size - 1]));
	--php->size;

	AdjustDown(php->a, php->size, 0);

}

4、堆的实现code

heap.h

#pragma once
#include<assert.h>
#include<stdlib.h>
#include<stdio.h>

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

void HeapInit(HP* php);
void HeapDestory(HP* php);
void HeapPush(HP* php, int child);
void HeapPop(HP* php);
void AdjustUp(HPDataType* a, int child);
void AdjustDown(HPDataType* a, int n, int parent);   //堆  堆中元素个数  根节点的索引值
void Swap(int* child, int* parent);
void HeapPrint(HP* php);

heap.c

#include"heap.h"

//初始化
void HeapInit(HP* php)
{
	assert(php != NULL);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}

//销毁
void HeapDestory(HP* php)
{
	assert(php != NULL);

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

void HeapPrint(HP* php)
{
	assert(php != NULL);
	for (int i = 0; i < php->size; ++i)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

void Swap(int* child, int* parent)
{
	int temp = *parent;
	*parent = *child;
	*child = temp;
}

//向上调整
void AdjustUp(HPDataType* 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;
		}
	}
}

//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)       //由于这里的n为元素的个数所以child最大为n-1 
	{
		if ((child +1 < n)&&(a[child] > a[child + 1]))  //如果大于则左孩子变右孩子
		{
			++child;
		}

		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}	
}

//堆中插入数据
void HeapPush(HP* php, int child)
{
	assert(php != NULL);
	if (php->size == php->capacity)  //如果容量已满,扩容
	{
		//扩容
		int newCapacity = ((php->capacity == 0) ? 4 : sizeof(php->a) * 2);
		HPDataType* ptr = (HPDataType*)realloc(php->a, sizeof(HPDataType)*newCapacity);
		if (ptr == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = ptr;
		php->capacity = newCapacity;
	}

	php->a[php->size] = child;   //插入数据
	php->size++;
	AdjustUp(php->a, php->size -1);     //向上调整是对已有的数据通过其索引来找到对应的值进行调整,所有只需要传入php->a以及尾插数据的索引值
}


//pop堆中数据
void HeapPop(HP* php)
{
	assert(php != NULL);

	Swap(&(php->a[0]), &(php->a[php->size - 1]));
	--php->size;

	AdjustDown(php->a, php->size, 0);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值