堆的应用(1000个数据中找最大的前K个元素,堆排序)

(1)从1000个数据中找到k个最大数据

首先看到这个题时,可能会想到先将这1000个数据进行降序排序,即取出的前k个元素最大。时间复杂度为O(N^2),使得程序效率低。

如何解决这个问题呢?我们的堆就派上用场喽!

解题思路:

可先创建一个数组topK[k],将100w中的前k个数据放入数组topK中,将topK中的数据建小堆,则可保证堆的第一个元素是最小的,将第k个元素与堆中第一个元素比较,若大于,则交换。对堆进行重新建小堆,取第k+1个元素与堆中第一个元素比较,以此类推,直至100w-k个元素比较完。则此时堆中的元素就是k个最大数据。

代码实现:

const int N = 1000;
const int K = 100;

void AdjustDown(int topK[],int parent) //建小堆
{
	int child = 2*parent+1;
	while(child < K)
	{
		if(child+1 < K && topK[child+1] < topK[child])
		{
			++child;
		}
		if(topK[child] < topK[parent])
		{
			swap(topK[child],topK[parent]);
			parent = child;
			child = 2*parent+1;
		}
		else
		{
			break;
		}
	}
}
void GetTopK(int a[],int topK[])
{
	assert(a);
	int i = 0;
	for(i=0;i<K;++i) //将a的前K个元素放入数组中
	{
		topK[i] = a[i];
	}
	for(i=(K-2)/2;i>=0;--i)//对前K个元素建小堆
	{
		AdjustDown(topK,i);
	}
	for(i=K;i<N;++i)
	{
		if(a[i] > topK[0]) //将K个元素之后的每个元素和堆的第一个元素(最小元素)比较
		{
			swap(a[i],topK[0]);//若比第一个元素大,则交换
			AdjustDown(topK,0);//对新堆重新建小堆
		}
	}
}

测试代码:

void Test2()
{
	int topK[K];
	int a[N];
	srand(time(0)); //随机数种子
	for(int i=0;i<N;++i)
	{
		a[i] = rand(); //随机数
	}
	GetTopK(a,topK);
	for(int i=0;i<K;i++)
	{
		cout<<topK[i]<<" ";
	}
}

测试结果:

时间复杂度为 k*lgk+N*lgk

当N庞大时,k可忽略,则时间复杂度为O(N),大大提高了效率。

(2)堆排序

既然是排序,那就有两种可能升序or降序。使得堆是建大堆方便还是建小堆方便。

<1>若为升序,则需要建大堆。

堆的第一个元素为最大,将最大元素与末尾元素交换,剩下的元素(除末尾元素)向下调整。

<2>若为降序,则需要建小堆。

堆的第一个元素为最小,将最小元素与末尾元素交换,剩下的元素(除末尾元素)向下调整。

代码实现(以升序为例):

void AdjustDown(int a[],int parent,int size)  //建大堆
{
	assert(a);
	int child = 2*parent+1;
	while(child < size)
	{
		if(child+1 < size && a[child+1] > a[child])
		{
			++child;
		}
		if(a[child] > a[parent])
		{
			swap(a[child],a[parent]);
			parent = child;
			child = 2*parent+1;
		}
		else
		{
			break;
		}
	}
}
void HeapSort(int a[],int size) 
{
	assert(a);
	//建堆
	for(int i=(size-2)/2;i>=0;--i)
	{
		AdjustDown(a,i,size);  //建大堆
	}

	//选择最大的放到末尾,从剩下数据向下调整
	for(int i=0;i<size;i++)
	{
		swap(a[0],a[size-1-i]);
		AdjustDown(a,0,size-1-i);
	}
}

//测试代码
void TestHeapSort()
{
	int a[] = {10,16,18,12,11,13,15,17,14,19};
	int size = sizeof(a)/sizeof(a[0]);
	HeapSort(a,size);
}

测试结果:

时间: 2024-08-09 00:56:25

堆的应用(1000个数据中找最大的前K个元素,堆排序)的相关文章

算法导论学习之线性时间求第k小元素+堆思想求前k大元素

对于曾经,假设要我求第k小元素.或者是求前k大元素,我可能会将元素先排序,然后就直接求出来了,可是如今有了更好的思路. 一.线性时间内求第k小元素 这个算法又是一个基于分治思想的算法. 其详细的分治思路例如以下: 1.分解:将A[p,r]分解成A[p,q-1]和A[q+1,r]两部分.使得A[p,q-1]都小于A[q],A[q+1,r]都不小于A[q]; 2.求解:假设A[q]恰好是第k小元素直接返回,假设第k小元素落在前半区间就到A[p,q-1]递归查找.否则到A[q+1,r]中递归查找. 3

大数据中找上中位数的方法

题目: 40亿 大整数,组成了一个大文件.想找到其中的 上中位数该怎么办?内存:10MB,怎么办?内存:20K,怎么办?内存:有限几个字符,怎么办?条件:按行读取文件,读取操作不占用内存. 应该具备的能力:2^k = ? 应该都能够熟记,达到反射性反应的程度.字节数 对应计算机中的 容量(T, G, M, K) 内存只有 10MB 的情况接下来我们来解题:看到大数据容量限制的,首先想到的是从范围入手.1. 数据是 有符号? / 无符号?2. 我们知道一个 4字节的无符号整数 范围为:0~42亿

在百万数据中找出重复的数据sql

select * from (select count(name) as isone, name from tbl_org_departments group by name) t where t.isone > 1; 执行子句时结果: 下面是没使用分组的时候结果

一组数据中找出一个唯一出现过一次的数字

利用同一数字的异或为0的关系,创建了一个函数 #include<stdio.h> #include<stdlib.h> int Fac(int n, int a[10])//创建异或函数 { //相等的数字异或之后就成0了 int temp = 0;//必须先初始化0,或a[0] int i; for (i = 0; i < n; i++) { temp ^= a[i]; } return temp; } int main() { int i, n; int ret; int

数组中找出最小的K个数

题目 给出一个数组,找出K个最小的值 例如给出数组{5,2,4,3,1},给定K值3,则输出结果为{2,3,1} 程序 先给出第一个版本的程序 1 public static void printKNum(int[] source, int k) {//算法入口 2 if (k <= 0) { 3 System.out.println("请出入合法的K值"); 4 } else if (source.length <= k) {//如果数组的长度小于等于K,则全部输出 5

在链表中找出倒数第K个节点

(1)遍历两遍,第一次计算出链表长度n,第二次找到(n-k)个节点,也就是倒数第K个节点. (2)遍历一遍,定义两个指针,一个指针fast,一个指针slow,都指向头结点,fast指针先向前走K,然后再同时遍历,当fast遍历到最后一个节点时,slow所指向的节点就是倒数第K个节点. #include<stdio.h> #include<stdlib.h> #include<assert.h>   struct Listnode {     int _value;   

找出N个数据中的最大的K个数据---堆排序

从N个数据中找出最大的K个数据,而且这里有一个限制:内存里存不下所有的N个数据,但是可以存下K个数据.这就让我们打消了用排序的方法来解的念头. 在这里我们使用堆排序来完成. 因为我们只能有K个数据那么大的空间,所以我们建一个K大的堆,将N的前K个数据插入到堆中,然后调整堆.(对于堆结构不了解的可以查看我微博  http://helloleex.blog.51cto.com/10728491/1768758) 对于最大的K个数据,我们要怎么找出来呢.我们首先要确定我们是要用大顶堆还是小顶堆.  用

【海量数据处理】N个数中找出最大的前K个数

N个数中找出最大的前K个数,需要用小堆实现. 分析:由于小堆的堆顶存放堆中最小的数据,可以通过与堆顶数据进行比较,将大数据存放在堆中,注意在每次改变堆顶数据后,进行调堆,使堆顶一直存放整个堆中最小元素. void AdjustDown(int *a, size_t root, size_t size)//下调 {//小堆 size_t parent = root; size_t child = parent * 2 + 1; while (child < size) { if (child + 

面试题-10亿个数中找出最大的10000个数(top K问题)

一个较好的方法:先拿出10000个建立小根堆,对于剩下的元素,如果大于堆顶元素的值,删除堆顶元素,再进行插入操作,否则直接跳过,这样知道所有元素遍历完,堆中的10000个就是最大的10000个.时间复杂度: m + (n-1)logm = O(nlogm) 优化的方法:可以把所有10亿个数据分组存放,比如分别放在1000个文件中(如果是字符串hash(x)%M).对每个文件,建立大小为10000的小根堆,然后按有序数组的合并合并起来,取出最大的10000个即是答案. top K问题 在大规模数据