二叉树-堆

本文介绍了堆的概念,包括小堆和大堆的定义,以及堆的性质。详细阐述了堆的实现过程,包括向下调整、创建堆、插入元素、删除元素等操作。同时提到了堆在堆排序中的应用。

一、堆的概念及结构

如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。
  • 堆顶元素一定是最大的或最小的在这里插入图片描述
    在这里插入图片描述

二、堆的实现

1、堆向下调整算法
现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

int a[] = {27,15,19,18,28,34,65,49,25,37};

//思路
 //1、找parent较小的孩子
 int child=parent*2+1;//parent的左孩子
 while(child<size){
 //保证右孩子存在:child+1<size
 if(child+1<size&&array[child+1]<array[child]) 	
 	child+=1;
 //检测parent是否满足堆得性质
 if(array[child]<array[parent])
 	Swap(&array[parent],&array[child]);
//双亲比孩子大,交换完成后,较大的双亲往下移动,可能会导致字数不满足堆的性质
 	parent=child;
 	child=parent*2+1else
 	return}

在这里插入图片描述
2、堆的创建
下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。

int a[] = {1,5,3,8,7,6};

在这里插入图片描述

3、堆的插入
先插入一个80到数组的尾上,再进行向上调整算法,直到满足堆。
在这里插入图片描述
4、 堆的删除
删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
在这里插入图片描述
5 、堆排序
在这里插入图片描述

三、堆得代码实现

typedef int HDataType;

int Less(HDataType left, HDataType right);
int Greater(HDataType left, HDataType right);

//函数指针变量:可以指向任何有两个HDataType参数以及一个int类型返回值得所有函数
//cmp势函数指针变量,Cmp可以指向Less,也可以指向Greater
//Cmp的类型:int(*)(HDataType,HDataType)

static int (*Cmp)(HDataType left, HDataType right);
typedef int(*CMP)(HDataType, HDataType);

typedef struct Heap
{
	HDataType* array;
	int capacity;
	int size;
	CMP Cmp;//函数指针变量
}Heap;

int Less(HDataType left, HDataType right)
{
	return left < right;
}
int Greater(HDataType left, HDataType right)
{
	return left > right;
}
//初始化一个空堆
void HeapInit(Heap* hp, int initCap, CMP cmp)
{
	assert(hp);
	initCap = initCap < 10 ? 10 : initCap;
	hp->array = (HDataType*)malloc(sizeof(HDataType) * initCap);
	if (NULL == hp->array)
	{
		assert(0);
		return;
	}
	hp->capacity = initCap;
	hp->size = 0;
	//接受比较方式
	hp->Cmp = cmp;
}
void Swap(HDataType* left, HDataType* right)
{
	HDataType temp = *left;
	*left = *right;
	*right = temp;
}
void AdjustDown(Heap* hp, int parent)
{
	//用child标记娇小的孩子
	int size = hp->size;
	int* array = hp->array;
	int child = parent * 2 + 1;
	while (child < size)
	{
		//找到parent中两个孩子中娇小的孩子
		if (child + 1 < size && hp->Cmp(array[child + 1],array[child]))//array[child + 1] > array[child]大堆
		{
			child += 1;
		}
		//parent娇小的孩子已经找到
		//检测parent是否满足堆得性质
		if (hp->Cmp(array[child] , array[parent]))//array[child] > array[parent]
		{
			Swap(&array[child], &array[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			return;
	}
}
//用数组中的元素创建堆
void HeapCreate(Heap* hp, int* array, int size, CMP cmp)
{
	//1、先将数组的元素放入堆中
	HeapInit(hp, size, cmp); 
	memcpy(hp->array, array, sizeof(HDataType*) * size);
	hp->size = size;
	//2、进行调整
	for (int root = (size - 2) / 2; root >= 0; root--)
	{
		AdjustDown(hp, root);
	}
}
void AdjustUP(Heap* hp, int child)
{
	HDataType* array = hp->array;
	int size = hp->size;
	int parent = ((child - 1) >> 1);
	while (child)
	{
		if (hp->Cmp(array[child] ,array[parent]))
		{
			Swap(&array[child], &array[parent]);
			child = parent;
			parent = ((child - 1) >> 1);
		}
		else
		{
			return;
		}
	}
}
void CheckCapacityHP(Heap* hp)
{
	assert(hp);
	if (hp->size == hp->capacity)
	{
		int newCapacity = hp->capacity * 2;
		HDataType* temp = (HDataType*)malloc(sizeof(HDataType) * newCapacity);
		if (temp == NULL)
		{
			assert(0);
			return;
		}
		//memcpy(temp, hp->array, hp->size * sizeof(HDataType));
		for (int i = 0; i < hp->size; ++i)
			temp[i] = hp->array[i];
		free(hp->array);
		hp->array = temp;
		hp->capacity = newCapacity;
	}
}
void HeapPush(Heap* hp, HDataType data)
{
	assert(hp);
	//0、检测是否需要扩容
	CheckCapacityHP(hp);

	//1、将新元素插入数组的末尾,将元素插入到完全二叉树最后
	hp->array[hp->size++] = data;
	//2、新元素插入后,可能会破坏堆得性质
	AdjustUP(hp, hp->size - 1);

}
void HeapPop(Heap* hp)
{
	if (HeapEmpty(hp))
		return;

	Swap(&hp->array[0], &hp->array[hp->size - 1]);
	hp->size--;
	AdjustDown(hp,0);
}
//获取堆顶元素
HDataType HeapTop(Heap* hp)
{
	assert(!HeapEmpty(hp));
	return hp->array[0];
}

//堆为空返回非0值
int HeapEmpty(Heap* hp)
{
	assert(hp);
	return 0 == hp->size;
}
int HeapSize(Heap* hp)
{
	assert(hp);
	return hp->size;
}
void HeapDestroy(Heap* hp)
{
	assert(hp);
	free(hp->array);
	hp->array = NULL;
	hp->capacity = 0;
	hp->size = 0;
}
void TestHeap()
{
	int array[] = { 3,6,9,1,5,2,0,7,8,4 };
	Heap hp;
	HeapCreate(&hp, array, sizeof(array) / sizeof(array[0]),Less);
	printf("heap size=%d\n", HeapSize(&hp));
	printf("heap top=%d\n", HeapTop(&hp));

	HeapPop(&hp);
	printf("heap size=%d\n", HeapSize(&hp));
	printf("heap top=%d\n", HeapTop(&hp));

	HeapPush(&hp, 0);
	HeapDestroy(&hp);
}
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值