C语言-小堆的简单实现(基于完全二叉树)

目录

一、二叉树简介:

1.完全二叉树:

2.非完全二叉树:

二、此帖的堆是用动态数组来实现:

1、先拟定好堆的功能有哪些:

Heap.h(头文件):

Heap.c(函数文件):

2、部分函数的详细介绍

1.AdjustUp函数的介绍:

2.AdjustDown函数的介绍:


一、二叉树简介:

这里只是简单介绍一下完全二叉树非完全二叉树:

1.完全二叉树:

完全二叉树是连续的,像数组一样:(可以看图了解)

2.非完全二叉树:

而非完全二叉树是不连续的:(可以看图了解)

二、此帖的堆是用动态数组来实现:

用动态数组的原因是,因为静态数组无法扩容。

parent---父亲(或者母亲)

child---子女

1、先拟定好堆的功能有哪些:
Heap.h(头文件):

这里以小堆为例子,如何实现大堆,请看后续:

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

typedef int HPDataType;

typedef struct Heap
{
	int* val; // 这里采用动态数组来存储数据
	int size; // 记录堆的有效元素
	int capacity; // 记录堆的空间大小
}Heap;

void HeapInit(Heap* hp);// 堆的初始化

void HeapCreate(Heap* hp, HPDataType* a, int n);// 此函数是把静态数组中的数据移到动态数组中,详细请看后面Heap.c文件描述

void HeapDestory(Heap* hp);// 堆的销毁

void HeapPush(Heap* hp, HPDataType x);// 堆的插入
 
void HeapPop(Heap* hp);// 堆的删除
 
HPDataType HeapTop(Heap* hp);// 取堆顶的数据
 
int HeapSize(Heap* hp);// 堆的数据个数
 
int HeapEmpty(Heap* hp);// 堆的判空
 
void AdjustUp(HPDataType* hp, int child);// 堆元素的向上调整
 
void AdjustDown(HPDataType* hp, int parent, int n);// 堆元素的向下调整
 
void Swap(HPDataType* p1, HPDataType* p2);// 元素交换
Heap.c(函数文件):
#include "Heap.h" // 别忘了头文件的引用

void HeapInit(Heap* hp)	//初始化
{
	assert(hp);

	hp->val = NULL;
	hp->capacity = 0;
	hp->size = 0;
}	

void HeapCreate(Heap* hp, HPDataType* a, int n) // 此函数是把静态数组中的数据移到动态数组中
{
    //Heap* hp -> 堆指针
    // HPDataType* a -> 是静态数组的指针
    // int n-> 是静态数组的空间大小
	HPDataType* tmp = (HPDataType*)realloc(hp->val, sizeof(HPDataType) * n);
    // 静态数组不支持realloc函数操作,故此必须重开动态空间
	hp->val = tmp;
	for (int i = 0; i < n; i++)
	{
		tmp[i] = a[i];
		AdjustUp(hp->val, i);
		hp->size++;
	}
	hp->capacity = n;
}

void HeapDestory(Heap* hp)    // 销毁掉堆
{
	assert(hp);

	free(hp->val);
	hp->val = NULL;
	hp->capacity = 0;
	hp->size = 0;
}

void HeapPush(Heap* hp, HPDataType x)    // 向堆中插入元素
{
	if (hp->size == hp->capacity)  //堆的有效元素和最大空间是否相等,来判断堆是否满
	{
		int Newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(hp->val, sizeof(HPDataType) * Newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		hp->val = tmp;
		hp->capacity = Newcapacity;
	}

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

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

void HeapPop(Heap* hp)  // 删除堆顶的元素
{
	assert(hp);
	assert(hp->size > 0);

	Swap(&hp->val[0], &hp->val[hp->size - 1]);
	hp->size--;
	AdjustDown(hp->val, 0, hp->size);
}

HPDataType HeapTop(Heap* hp)  // 访问堆顶的元素,但不删除
{
	assert(hp);
	assert(hp->size > 0);

	return hp->val[0];
}

int HeapSize(Heap* hp)  // 获取堆的有效元素个数
{
	assert(hp);
	assert(hp->size > 0);

	return hp->size;
}

int HeapEmpty(Heap* hp)  // 判断堆是否为空
{
	assert(hp);

	return hp->size == 0;
}

void AdjustUp(HPDataType* hp, int child)  // 让子节点与父结点比较,从而到达理想位置
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (hp[parent] > hp[child])
		{
			Swap(&hp[child], &hp[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}


void AdjustDown(HPDataType* hp, int parent, int n)  // 让父节点与子结点比较,从而到达理想位置
{  
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && hp[child] > hp[child + 1])
		{
			++child;
		}
		if (hp[parent] > hp[child])
		{
			Swap(&hp[parent], &hp[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void Swap(HPDataType* p1, HPDataType* p2)  // 两个结点的数据交换
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
2、部分函数的详细介绍
1.AdjustUp函数的介绍:
void AdjustUp(HPDataType* hp, int child)
{
	int parent = (child - 1) / 2; // 该写法是依据子节点的下标反向推断出父节点的下标
	while (child > 0) // 当child等于parent时就说明已经到根结点了
	{
		if (hp[parent] > hp[child])
		{
			Swap(&hp[child], &hp[parent]); // 交换数据函数
			child = parent; 
			parent = (child - 1) / 2;
            // 当交换成功时,将再往上走一层,继续判断交换后的数据(此时从父节点更变为子节点)与其父节点比大小
		}
		else
		{
			break;
		}
	}
}

2.AdjustDown函数的介绍:

可以参考上图理解AdjustDown函数的思路

void AdjustDown(HPDataType* hp, int parent, int n)  // 让父节点与子结点比较,从而到达理想位置
{ 
    // 该写法是依据父节点的下标推断出子节点的下标
    // 这里假设左子节点是满足交换条件的.
	int child = parent * 2 + 1;

	while (child < n)
	{

        // 再让左子节点和右子节点比较,筛选出满足条件的那个子节点
        // child + 1 < n 是为了防止越界
        if (child + 1 < n && hp[child] > hp[child + 1]) 
		{
			++child;
		}
		if (hp[parent] > hp[child])
		{
			Swap(&hp[parent], &hp[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

如果想要大堆,那就把上述两个函数中 if 语句中的元素数据之间比大小反一下就好了

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值