海量数据处理: Top K算法(问题) 小顶堆实现

问题描述:有N(N>>10000)个整数,求出其中的前K个最大的数。

方法一:

思路:先将前K个数据构造成一个小顶堆,对于接下来的每一个数,与根元素进行比较,若<=根结点,则舍弃,反之则用该数值替换根结点数值,并进行最小堆的调整。

建堆的时间复杂度为O(K),每次调整堆的时间复杂度为O(logK),so总的时间复杂度为O(nlogK);

void Adjust_dui(int* a, int s, int k)
{
	int j = 0;
	int tmp = a[s];
	for (j = 2 * s+1; j <= k - 1; j = 2 * j + 1)
	{
		if (j<k - 1 && a[j]>a[j + 1])
			j++;
		if (tmp < a[j]) break;
		a[s] = a[j];
		s = j;
	}
	a[s] = tmp;
}
void create_dui(int* a, int k)
{
	for (int j = k / 2 - 1; j >= 0; j--)
	{
		Adjust_dui(a, j, k);
	}
}
void main()
{
	int a[] = { 4, 2, 1, 5, 6, 9,0, 3, 8, 7 };
	int k = 5;
	int len = sizeof(a) / sizeof(int);
	create_dui(a, k);

	for (int i = k; i < len; ++i)
	{
		if (a[i]>a[0])
		{
			a[0] = a[i];
			Adjust_dui(a, 0, k);
		}
	}
}

方法二:采用快速选择的算法,可以达到O(n)的时间复杂度;

对于类似的问题,例如,给定一个数组,最最小的K个元素,可采用快速选择算法,关键在于枢纽值的选取,选取的好了,就可以达到O(n)的时间复杂度;

///下面的代码对July博客中的三个版本代码进行重新改写。欢迎指出错误。    
///先把它们贴在这里,还要进行随机化数据测试。待发...    
    
//modified by 飞羽 at 2011.5.11    
/Top_K_test    
    
//修改了下命名规范,July、updated,2011.05.12。    
#include <iostream>    
#include <stdlib.h>    
using namespace std;    
    
inline int my_rand(int low, int high)    
{    
    int size = high - low + 1;    
    return  low + rand() % size;    
}    
    
int partition(int array[], int left, int right)    
{    
    int pivot = array[right];    
    int pos = left-1;    
    for(int index = left; index < right; index++)    
    {    
        if(array[index] <= pivot)    
            swap(array[++pos], array[index]);    
    }    
    swap(array[++pos], array[right]);    
    return pos;//返回pivot所在位置    
}    
    
bool median_select(int array[], int left, int right, int k)    
{    
    //第k小元素,实际上应该在数组中下标为k-1    
    if (k-1 > right || k-1 < left)       
        return false;    
    
    //真正的三数中值作为枢纽元方法,关键代码就是下述六行    
    int midIndex=(left+right)/2;    
    if(array[left]<array[midIndex])    
        swap(array[left],array[midIndex]);    
    if(array[right]<array[midIndex])    
        swap(array[right],array[midIndex]);    
    if(array[right]<array[left])    
        swap(array[right],array[left]);    
    swap(array[left], array[right]);    
        
    int pos = partition(array, left, right);    
        
    if (pos == k-1)    
        return true;    
    else if (pos > k-1)    
        return median_select(array, left, pos-1, k);    
    else return median_select(array, pos+1, right, k);    
}    
    
bool rand_select(int array[], int left, int right, int k)    
{    
    //第k小元素,实际上应该在数组中下标为k-1    
    if (k-1 > right || k-1 < left)       
        return false;    
    
    //随机从数组中选取枢纽元元素    
    int Index = my_rand(left, right);    
    swap(array[Index], array[right]);    
        
    int pos = partition(array, left, right);    
        
    if (pos == k-1)    
        return true;    
    else if (pos > k-1)    
        return rand_select(array, left, pos-1, k);    
    else return rand_select(array, pos+1, right, k);    
}    
    
bool kth_select(int array[], int left, int right, int k)    
{    
    //直接取最原始的划分操作    
    if (k-1 > right || k-1 < left)       
        return false;    
    
    int pos = partition(array, left, right);    
    if(pos == k-1)    
        return true;    
    else if(pos > k-1)    
        return kth_select(array, left, pos-1, k);    
    else return kth_select(array, pos+1, right, k);    
}    
    
int main()    
{    
    int array1[] = {7, 8, 9, 54, 6, 4, 11, 1, 2, 33};     
    int array2[] = {7, 8, 9, 54, 6, 4, 11, 1, 2, 33};     
    int array3[] = {7, 8, 9, 54, 6, 4, 11, 1, 2, 33};     
        
    int numOfArray = sizeof(array1) / sizeof(int);    
    for(int i=0; i<numOfArray; i++)    
        printf("%d/t",array1[i]);    
        
    int K = 9;    
    bool flag1 = median_select(array1, 0, numOfArray-1, K);    
    bool flag2 = rand_select(array2, 0, numOfArray-1, K);    
    bool flag3 = kth_select(array3, 0, numOfArray-1, K);    
    if(!flag1)     
        return 1;    
    for(i=0; i<K; i++)    
        printf("%d/t",array1[i]);    
    printf("/n");    
        
    if(!flag2)     
        return 1;    
    for(i=0; i<K; i++)    
        printf("%d/t",array2[i]);    
    printf("/n");    
        
    if(!flag3)     
        return 1;    
    for(i=0; i<K; i++)    
        printf("%d/t",array3[i]);    
    printf("/n");    
        
    return 0;    
}    


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值