栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

堆-堆排序-TopK问题

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

堆-堆排序-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个最大值。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/934001.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号