堆排序和Top-K问题

目录

一、堆排序(降序为例)

一、建堆

二、排序

二、解决Top-K问题

一、Top-K是什么

二、如何解决Top-K

三、代码实现 


一、堆排序(降序为例)

一、建堆

有一个数组a,数组中有n个元素,首先将要排序的数组调整为小堆,有以下两种思路:

①向上调整建堆

我们以a[0]为堆顶元素,依次插入后面的数并且向上调整,从上往下逐步完善这个小堆

for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}


void AdjustUp(int * a, int child)
{
	int parent = (child-1) / 2;
	while ( child>0 )
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);

			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

②向下调整建堆

从最后一个数据的双亲节点开始向下调整,从下往上逐步完善这个小堆

for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}


//向下调整
void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child<n)
 	{
		//选左右孩子大/小的那一个
		if (child + 1 < n && a[child+1] < a[child])
		{
			child++;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

二、排序

我们已经建立了一个小堆,如何将这个小堆进行降序排序呢?

我们可以将堆顶元素与最后一个元素进行交换,这个时候最后一个元素是最小的元素,同时将前n-1个数据看成堆,对堆顶元素进行向下调整,这个时候堆顶元素是次小的元素,再与第n-1个元素进行交换,重复上面步骤,这个时候倒数第二个是次小的元素。一直到最后我们完成了降序排序。

int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}

二、解决Top-K问题

一、Top-K是什么?

 

像我们熟悉的战力排行榜,年级前十名等等,就是我们常说的Top-K问题。

Top-K问题:求数据中前K个最大的元素或者最小的元素

二、如何解决Top-K

解决Top-K问题呢,主要有三个步骤,下面我们假设取前K个最大的数

1.建立含K个数据的小堆

2.后面的数依次与堆顶元素作比较,如果比堆顶元素大,则用它替换堆顶元素,替换后向下调整重新变为一个小堆

3.经过上面两个步骤,我们建的小堆已经包含了前K个最大的数,接下来就是对这个堆进行堆排序

三、代码实现 

void PrintPopK(int* a, int n, int k)
{
	//建立K个数的堆(前K个最大的,建小堆;前K个最小的,建大堆)
	for (int i = (k - 2) / 2; i >= 0; i--)
	{
		AdjustDown(a, k, i);
	}
	//比较
	for (int i = k; i < n; i++)
	{
		if (a[i] > a[0])
		{
			a[0] = a[i];
			AdjustDown(a, k, 0);
		}
	}
	//排序
	int end = k - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
	for (int i = 0; i < k; i++)
	{
		cout << a[i] << ' ';
	}
}



//交换
void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

今天的分享就到这里啦,大家觉得有用的话,关注关注点个赞吧!!! 

欢迎各位优秀的uu评论区指正~

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值