数据结构:堆的实现

目录

什么是堆?

堆的类型

   1、大堆

        2、小堆

堆的实现

        代码思路:

        代码实现:

        1、堆的创建、初始化和销毁

        2、 堆的插入

         3、堆的删除

        4、取堆顶的数据

        5、取堆的数据个数

        6、判空


什么是堆?

        堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。

   堆的性质:

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

(更详细的定义->堆(数据结构)_百度百科)  


堆的类型

   1、大堆

        这是一个大堆,我们可以发现它们数值的大小总是小于上一级的,并且大于下一级的,与同级的无关;同时它们的上一级是(自身的位置 - 1)/ 2,且自身的下一级为(自身*2+1)或者(自身*2+2)。

        2、小堆

        

         这是一个小堆,我们可以发现它们数值的大小总是大于上一级的,并且小于下一级的,且与同级的无关;同时它们的上一级是(自身的位置 - 1)/ 2,且自身的下一级为(自身*2+1)或者(自身*2+2)。


堆的实现

        代码思路:

        (我们以上面的大堆理论模型例子)我们可以发现,(父级 * 2)+  1 和(父级 * 2)  + 2是它的左孩子和右孩子,且(孩子 - 1)/ 2,所以我们可以使用柔性数组来实现堆。在理论上,堆是像树一样分布着,但实际上,它在计算机里是紧密挨着排布的。

        当我们要插入一个数的时候,先插入柔性数组里,然后再比较父子级的大小关系,从而来调整堆。

        当我们要删除一个数据的时候,先交换柔性数组里的首位数据,然后再进行调整。


        代码实现:

        (以建立大堆为例)

//设计柔性数组
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;

//交换数据
void Swap(HPDataType* a, HPDataType* b)
{
	HPDataType tmp = *a;
	*a = *b;
	*b = tmp;
}

//堆的初始化
void HeapInit(Heap* php)
{
	assert(php);

	php->_a = NULL;
	php->_size = php->_capacity = 0;
}

// 堆的销毁
void HeapDestory(Heap* php)
{
	assert(php);

	free(php->_a);
	php->_a = NULL;
	php->_size = php->_capacity = 0;
}

//向上调整
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(Heap* php, HPDataType x)
{
	assert(php);

	if (php->_capacity == php->_size)
	{
		int capacity = php->_capacity == 0 ? 4 : php->_capacity * 2;
		HPDataType* new = (HPDataType*)realloc(php->_a,sizeof(HPDataType) * capacity);
		if (new == NULL)
		{
			perror("HeapPush::() malloc fail");
			return;
		}
		php->_capacity = capacity;
		php->_a = new;
	}

	php->_a[php->_size] = x;
	php->_size++;
	AdjustUp(php->_a, php->_size - 1);
}

//向下调整
void AdjustDown(HPDataType* a, int parent, int n)
{
	assert(a);
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child++;
		}

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

// 堆的删除
void HeapPop(Heap* php)
{
	assert(php);
	assert(php->_size > 0);

	php->_size--;
	Swap(&php->_a[0], &php->_a[php->_size]);

	AdjustDown(php->_a, 0, php->_size);
}

// 取堆顶的数据
HPDataType HeapTop(Heap* php)
{
	assert(php);

	return php->_a[0];
}

// 堆的数据个数
int HeapSize(Heap* php)
{
	assert(php);

	return php->_size;
}

// 堆的判空
int HeapEmpty(Heap* php)
{
	assert(php);

	return php->_size == 0;
}

        1、堆的创建、初始化和销毁

        当_size和_capacity是为了以后的扩容。

//设计柔性数组
typedef int HPDataType;//方便以后实现不同需求的堆
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;

//堆的初始化
void HeapInit(Heap* php)
{
	assert(php);

	php->_a = NULL;
	php->_size = php->_capacity = 0;
}

// 堆的销毁
void HeapDestory(Heap* php)
{
	assert(php);

	free(php->_a);
	php->_a = NULL;
	php->_size = php->_capacity = 0;
}

        2、 堆的插入

        每当我们插入一个数据,首先我们要对其柔性数组判断它是否还有空间,若空间不足就扩容,反之,则直接插入。

        最重要的来了,当插入完数据后,我们要对堆进行调整,这里我使用的是向上调整,根据我们上面父子级的关系可以先比较它们之间的大小是否要交换数据,若交换了,记得要将孩子到父亲的位置,父亲要调到它父亲的位置,继续比较,直至孩子到根。

//交换数据
void Swap(HPDataType* a, HPDataType* b)
{
	HPDataType tmp = *a;
	*a = *b;
	*b = tmp;
}

//向上调整
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(Heap* php, HPDataType x)
{
	assert(php);
    //当_size == _capacity相等的时候就扩容
	if (php->_capacity == php->_size)
	{
		int capacity = php->_capacity == 0 ? 4 : php->_capacity * 2;
		HPDataType* new = (HPDataType*)realloc(php->_a,sizeof(HPDataType) * capacity);
		if (new == NULL)
		{
			perror("HeapPush::() malloc fail");
			return;
		}
		php->_capacity = capacity;
		php->_a = new;
	}
    //插入数据
	php->_a[php->_size] = x;
	php->_size++;
    //向上调整
	AdjustUp(php->_a, php->_size - 1);
}

         3、堆的删除

        当我们要删除数据的时候,不是删除尾的数据,删除堆的尾是没有什么意义的,要删除堆的根的数据。

        要删除堆的根的数据。首先,我们要交换头尾的数据,然后再调整堆。因为我们要删除堆的时候,已经形成了堆,我们只需要将根的数据与它的子级计较即可。

//交换数据
void Swap(HPDataType* a, HPDataType* b)
{
	HPDataType tmp = *a;
	*a = *b;
	*b = tmp;
}

//向下调整
void AdjustDown(HPDataType* a, int parent, int n)
{
	assert(a);
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child++;
		}

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

// 堆的删除
void HeapPop(Heap* php)
{
	assert(php);
	assert(php->_size > 0);

	php->_size--;
	Swap(&php->_a[0], &php->_a[php->_size]);

	AdjustDown(php->_a, 0, php->_size);
}

        4、取堆顶的数据

        堆顶的数据就是柔性数组的第一个数据。

// 取堆顶的数据
HPDataType HeapTop(Heap* php)
{
	assert(php);

	return php->_a[0];
}

        5、取堆的数据个数

        直接返回柔性数组里的数据个数即可。


// 堆的数据个数
int HeapSize(Heap* php)
{
	assert(php);

	return php->_size;
}

        6、判空

// 堆的判空
int HeapEmpty(Heap* php)
{
	assert(php);
    //当柔性数组没有数据的时候就为空
	return php->_size == 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值