堆-堆排序-TopK问题

概念

堆是二叉树顺序存储的主要表现形式
其中任意一节点的值都>=或<=其孩子节点的值,称之为最大/小堆。
注: 堆一定是完全二叉树

堆的实现

1、向上调整

给一个堆中插入一个数据,如下图:
在这里插入图片描述
此处可采用向上调整,过程如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码实现:

void AdjustUp(HPDataType* a, int child)
{
	assert(a);
	//小堆
	while (child > 0)
	{
		int parent = (child - 1) / 2;
		if (a[child] < a[parent])//关键
		{
			Swap(&a[child], &a[parent]);
			child = parent;
		}
		else
			break;
	}
}

2、向下调整

目的也是调整为堆,但要求左右子树都是堆
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码实现:

void AdjustDown(HPDataType* a, int parent, int size)
{
	assert(a);

	int child = 2 * parent + 1;
	while (child < size)
	{
		//选小的为孩子
		//child + 1 < size:防止越界
		if (a[child + 1] < a[child] && child + 1 < size);
			child++;
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
			break;
	}
}

堆的创建

根据堆的性质,堆的创建可利用上述的向上调整和向下调整来建堆。

1、向上调整建堆

向上调整的函数声明:void AdjustUp(HPDataType* a, int child);
以下列数组为例: int a[] = { 27,15,19,18,28,34,65,49,25,37};

以调大堆为例,调堆过程如下:[在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现如下:
	for (int i = 1; i < sz; i++)
	{
		AdjustUp(a, i);
	}

如果child从a[sz]开始向上调整,对于调整过的部分不会有多次检查调整。
请添加图片描述
时间复杂度:O(N*log N)在这里插入图片描述

2、向下调整建堆

向下调整的函数声明:void AdjustDown(HPDataType* a, int parent, int size);
同样的数组为例: int a[] = { 27,15,19,18,28,34,65,49,25,37};
以调大堆为例,向下调堆过程如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现如下:
	for (int i = (sz - 1 - 1) / 2; i >= 0; i--)
	{
		//向下调整的前提是子树都是堆,
		//从倒数第一个非叶节点开始调
		//左右子树都可以调为堆
		AdjustDown(a, i, sz);
	}

时间复杂度:O(N)
在这里插入图片描述

堆的代码实现

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

堆的构建

void HeapInit(HP* p)
{
	assert(p);
	p->a = NULL;
	p->size = p->capacity = 0;
}

堆的销毁

void HeapDestroy(HP* p)
{
	assert(p);
	free(p->a);
	p->size = p->capacity = 0;
}

堆的插入

void HeapPush(HP* p, HPDataType d)
{
	assert(p);

	//插入数据与最后一个节点
	if (p->size == p->capacity)
	{
		//没空间了
		int newcapacity = p->size == 0 ? 4 : 2 * p->capacity;
		HPDataType* tmp = (HPDataType*)realloc(p->a, sizeof(HPDataType) * newcapacity);
		if (!tmp)
		{
			perror("HeapPush realloc");
			exit(-1);
		}
		p->a = tmp;
		p->capacity = newcapacity;
	}
	p->a[p->size++] = d;

	//调整
	AdjustUp(p->a, p->size - 1);

}

堆的删除

一般删除堆顶数据,删其他的没啥意义。
删除方式:将最后一个叶节点和堆的根节点互换,size–后,根节点的左右子树为堆,再向下调整。

void HeapPop(HP* p)
{
	assert(p);
	assert(p->size > 0);

	//交换
	
	//HPDataType tmp = p->a[0];
	//p->a[0] = p->a[0];
	//p->a[p->size - 1] = tmp;
	Swap(&p->a[0], &p->a[p->size - 1]);

	p->size--;

	AdjustDown(p->a, 0, p->size);
}

取堆顶的数据

HPDataType HeapTop(HP* p)
{
	assert(p);
	assert(p->size > 0);

	return p->a[0];
}

堆的数据个数

int HeapSize(HP* p)
{
	return p->size;
}

堆的判空

bool HeapEmpty(HP* p)
{
	assert(p);

	return p->size == 0;
}

TopK问题(找最大的前K个)

1、排序 O(N*log N)

利用上述的建堆方式,将堆顶数据pop——调整——pop,最后得到有序的数组。
:排升序,建大堆,大的数据pop后排在后面;反之,排降序,建小堆。

2、建立N个数的大堆,Top/Pop K 次 ,O(N+log N*K)
3、假设N很大,如N=100亿,K=100.

解决方案:
1、前k给数建立小堆
2、剩下N-K个数和堆顶比较,调整,最后堆中数据是前K个最大值。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值