目录
一、堆的性质
只要满足以下两点,它就是一个堆:
每个结点的值都大于或等于其左右孩子结点的值,我们叫做“大顶堆”。
每个结点的值都小于或等于其左右孩子结点的值,我们叫做“小顶堆”。

二、堆的相关基础操作
堆的创建
// 堆的构建
void HeaPCreate(Heap* hp, HPDataType* a, int n)
{
	//申请一个和传入的数组一样大的空间保存堆
	hp->arry = (HPDataType*)malloc(sizeof(HPDataType)*n);
	if (NULL == hp->arry)
	{
		assert(hp);
		return;
	}
	memcpy(hp->arry, a, n * 4);
	hp->capacity = n;
	hp->size = n;
	for (int root = (n - 2) / 2; root >= 0; root--)
    {
		//用向下调整从下向上(从第一个非叶子节点开始)依次对堆中的节点进行调整
		AjustdownHeap(hp,root);
	}
}
堆的插入
只能在尾插入然后进行向上调整
void HeapPush(HP* php, HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
        //申请新空间
		size_t newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);
		if (tmp == NULL)
		{
			printf("realloc failed\n");
			exit(-1);
		}
        //把a指向新的地址
		php->a = tmp;
        //更新容量
		php->capacity = newCapacity;
	}
    //将x放在堆尾
	php->a[php->size] = x;
	++php->size;
	// 向上调整,控制保持是一个小堆
	AdjustUp(php->a, php->size - 1);
}
void AdjustUp(HPDataType* a, size_t child)
{
    //找到他的父亲
	size_t parent = (child - 1) / 2;
    //到堆顶了就不需要再进行调整
	while (child > 0)
	{
        //如果孩子比父亲小,那就进行交换
		if (a[child] < a[parent])
			//if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void Swap(HPDataType* pa, HPDataType* pb)
{
	HPDataType tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}堆的删除
只能删除堆顶的元素,其方法是将堆顶的元素和堆尾进行交换,删除堆尾的元素,然后将堆顶元素进行向下调整
// 删除堆顶的数据。(最小/最大)
void HeapPop(HP* php)
{
	assert(php);
	assert(php->size > 0);
    
    //交换堆顶  堆尾元素
	Swap(&php->a[0], &php->a[php->size - 1]);
    //删除堆尾元素
	--php->size;
	AdjustDown(php->a, php->size, 0);
}
void AdjustDown(HPDataType* a, size_t size, size_t root)
{
	size_t parent = root;
    //默认是左孩子
	size_t child = parent * 2 + 1;
    //走到堆尾就结束,不用再进行调整
	while (child < size)
	{
		// 1、选出左右孩子中小的那个
		if (child + 1 < size && a[child + 1] < a[child])
		{
			++child;
		}
		// 2、如果孩子小于父亲,则交换,并继续往下调整
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}获取堆顶元素
HPDataType HeapTop(HP* php)
{
	assert(php);
    //数组长度大于0 才能运行,否则非法访问
	assert(php->size > 0);
	return php->a[0];
}堆的判空
bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}堆的有效元素个数
size_t HeapSize(HP* php)
{
	assert(php);
	return php->size;
}堆的销毁
void HeapDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = php->capacity = 0;
}
三、堆排序
建堆
升序:建大堆(不能建小堆)
降序:建小堆
我们可以直接对数组建堆,使数组满足堆的性质
例如:
int main()
{
	int a[] = { 4, 2, 7, 8, 5, 1, 0, 6 };
	HeapSort(a,sizeof(a)/sizeof(a[0]));
	for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
void HeapSort(int* a, int n)
{
	assert(a);
    //我们可以把数组看成依次插入n个数
    //每次对他们进行向上调整就可以形成一个堆
	/*for(int i = 1;i<n;i++)
	{
		AdjustUp(a, i);
	}*/
    
    //向下调整
	//找到最后一个数的父亲,然后进行向下调整
    //依次往上走,直到走到根节点
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
    size_t end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}2.top-k问题:
TOP-K 问题:即求数据结合中前 K 个最大的元素或者最小的元素,一般情况下数据量都比较大 。
 比如:专业前 10 名、世界 500 强、富豪榜、游戏中前 100 的活跃玩家等。
 对于 Top-K 问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了 ( 可能数据都不能一下子全部加载到内存中 ) 。最佳的方式就是用堆来解决,
基本思路如下:
 点个赞吧!










