[C语言]完全二叉树的应用-堆

结构体

typedef int Type;//自定义类型

typedef struct HNode//堆结构体
{
	Type *data;//存放堆的数组
	int NowSize;//目前堆的大小
	int MaxSize; //最大容量
} HNode;

创建空堆

HNode *CreatHeap()//创建一个空堆
{
	HNode *H = (HNode*)malloc(sizeof(HNode));
	H->data = (Type*)malloc(sizeof(Type)*(MAXSIZE+1));//因为从下标1开始存所以多申请一个空间
	H->NowSize = 0;
	H->MaxSize = MAXSIZE;
	H->data[0] = MAXDATA;//设置哨兵(其实也可以不用...)
	return H;
}

判空判满

int IsFull(HNode *H)//判满
{
	return H->NowSize==H->MaxSize;
}

int IsEmpty(HNode *H)//判空
{
	return H->NowSize == 0;
}

为堆中插入数值

void Swap(HNode *H,int a,int b)//交换H中两个值
{
	Type temp = H->data[a];
	H->data[a] = H->data[b];
	H->data[b] = temp;
}

int Insert(HNode *H,Type val)//插入值val
{
	if(IsFull(H))//判断是不是满了
		return ERROR;
	H->data[++H->NowSize] = val;//先放到最后一个位置
	int i = H->NowSize;
	int flag = 0;
	while(flag == 0)//往上调整
	{
		if(H->data[i]>H->data[i/2])//如果双亲比自己小就是不满足最大堆,那就交换双亲和自己的数值
			Swap(H,i,i/2);
		else//否则说明已经满足最大堆了
			flag = 1;
		i /= 2;//往上走
	}
	return OK;
}

向下调整满足最大堆

//num 为调整的开始下标, size为需要调整的堆的大小
void SiftDown(HNode *H,int num,int size)//向下调整(最大堆)
{
	int t,flag = 0;
	while(num * 2 <= size && flag == 0)//先判断有没有左孩子
	{
		t = H->data[num] >= H->data[num*2] ? num : num*2;//记录下num和它的左孩子数值 较大的那个的下标
		if(num*2+1 <= size)//如果有右孩子且右孩子更大,就记录右孩子的下标
			t = H->data[t] >= H->data[num*2+1] ? t : num*2+1;
		if(t == num)//如果发现自己比左右孩子都大说明已经满足最大堆了就让flag为1停止调整
			flag = 1;
		else//如果发现孩子比自己大就把最大的孩子和自己交换位置,然后继续往下
		{
			Swap(H,num,t);
			num = t;
		}

	}
}

删除并返回堆中最大值

int DelMax(HNode *H,Type *Maxval)//删除并用 Maxval 返回最大值
{
	if(IsEmpty(H))//看看是不是空的
		return ERROR;
	*Maxval = H->data[1];//因为满足最大堆所以堆顶的值最大,那就保存下来
	H->data[1] = H->data[H->NowSize--];//把最后一个数补到第一个位置--记得大小减少一个
	SiftDown(H,1,H->NowSize);//重新调整下让整体满足最大堆
	return OK;
}

堆排

前提是最大堆…
通过调用前面的向下调整函数,使每次删除掉最大值仍满足最大堆

//将无序的堆调整为最大堆
void MaxHeap(HNode *H)//从最后一个叶子节点的双亲开始(因为叶子节点不用往下调整)往前逐个调整为最大堆
{
	for(int i = H->NowSize/2; i>0; i--)
		SiftDown(H,i,H->NowSize);
}

//堆排--前提是满足最大堆
void Heapsort(HNode *H)
{
	for(int i = H->NowSize; i>1;)
	{
		Swap(H,1,i);//把目前最大的和目前最后一个数交换下位置,相当于把最大的数保存在最后一个位置
		i--;//把最后一个位置"保护"起来
		SiftDown(H,1,i);//调整使其满足最大堆
	}
}

完整代码

//堆:使用完全二叉树来存放数据,这时候适合用数组来构建,会很有规律 
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100 //最大数量 
#define MAXDATA 2147483646 //哨兵--最大堆就取一个所存储数据无法达到的最大值,这样就不用规定i>0,效率更高... 

#define ERROR 0
#define OK 1
typedef int Type;//自定义类型
typedef struct HNode//堆结构体
{
	Type *data;//堆数组
	int NowSize;//现在容量
	int MaxSize; //最大容量
} HNode;
HNode *CreatHeap()//创建一个空堆
{
	HNode *H = (HNode*)malloc(sizeof(HNode));
	H->data = (Type*)malloc(sizeof(Type)*(MAXSIZE+1));//因为从下标1开始存所以多申请一个空间
	H->NowSize = 0;
	H->MaxSize = MAXSIZE;
	H->data[0] = MAXDATA;//设置哨兵
	return H;
}
int IsFull(HNode *H)//判满
{
	return H->NowSize==H->MaxSize;
}
int IsEmpty(HNode *H)//判空
{
	return H->NowSize == 0;
}
void Swap(HNode *H,int a,int b)//交换H中两个值
{
	Type temp = H->data[a];
	H->data[a] = H->data[b];
	H->data[b] = temp;
}
//num 为调整的开始下标, size为需要调整的堆的大小
void SiftDown(HNode *H,int num,int size)//向下调整(最大堆)
{
	int t,flag = 0;
	while(num * 2 <= size && flag == 0)//先判断有没有左孩子
	{
		t = H->data[num] >= H->data[num*2] ? num : num*2;//记录下num和它的左孩子数值 较大的那个的下标
		if(num*2+1 <= size)//如果有右孩子且右孩子更大,就记录右孩子的下标
			t = H->data[t] >= H->data[num*2+1] ? t : num*2+1;
		if(t == num)//如果发现自己比左右孩子都大说明已经满足最大堆了就让flag为1停止调整
			flag = 1;
		else//如果发现孩子比自己大就把最大的孩子和自己交换位置,然后继续往下
		{
			Swap(H,num,t);
			num = t;
		}

	}
}
int Insert(HNode *H,Type val)//插入值val
{
	if(IsFull(H))//判断是不是满了
		return ERROR;
	H->data[++H->NowSize] = val;//先放到最后一个位置
	int i = H->NowSize;
	int flag = 0;
	while(flag == 0)//往上调整
	{
		if(H->data[i]>H->data[i/2])//如果双亲比自己小就是不满足最大堆,那就交换双亲和自己的数值
			Swap(H,i,i/2);
		else//否则说明已经满足最大堆了
			flag = 1;
		i /= 2;//往上走
	}
	return OK;
}
int DelMax(HNode *H,Type *Maxval)//删除并用 Maxval 返回最大值
{
	if(IsEmpty(H))
		return ERROR;
	*Maxval = H->data[1];//因为满足最大堆所以堆顶的值最大,那就保存下来
	H->data[1] = H->data[H->NowSize--];//把最后一个数补到第一个位置--记得大小减少一个
	SiftDown(H,1,H->NowSize);//重新调整下让整体满足最大堆
	return OK;
}
void MaxHeap(HNode *H)//从最后一个叶子节点的双亲开始(因为叶子节点不用往下调整)往前逐个调整为最大堆
{
	for(int i = H->NowSize/2; i>0; i--)
		SiftDown(H,i,H->NowSize);
}
void Heapsort(HNode *H)//堆排--前提是满足最大堆
{
	for(int i = H->NowSize; i>1;)
	{
		Swap(H,1,i);//把目前最大的和目前最后一个数交换下位置,相当于把最大的数保存在最后一个位置
		i--;//把最后一个位置"保护"起来
		SiftDown(H,1,i);//调整使其满足最大堆
	}
}
void Print(HNode *H)//展示
{
	for(int i = 1; i<=H->NowSize; i++)
		printf("%d ",H->data[i]);
}
void Free(HNode *H)//free
{
	free(H->data);
	free(H);
}
int main()
{
	HNode *H = CreatHeap();
	Type val;
	printf("输入需要插入的数字以q结束:");
	while(scanf("%d",&val)==1)
	{
		if(!Insert(H,val))
		{
			printf("NO Place");
			break;
		}
	}
	printf("\n存储的数字为:");
	Print(H);
	if(DelMax(H,&val))
	{
		printf("\n弹出最大的数字为:%d",val);
	}
	else
		printf("Empty\n");
	Heapsort(H);
	printf("\n从小到大排序为:");
	Print(H);
	Free(H);
	return 0;
}

效果图

在这里插入图片描述

参考《啊哈算法》《大话数据结构》

如果有错误请您指出,我一定洗耳恭听

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值