HDU 4006 The kth great number 堆/优先级队列/线段树

题意:先输入两个整数 n, k。然后有 n 行输入, I 代表写下一个数字, Q代表询问在所有写下的数字中第 k 大的数数多少?
题解:因为当写下的数少于 k 个是不存在询问的情况。所以可以建一个堆,然后没写下一个数字,若它比堆中的最小数字还小,则堆不做改变。若它比堆中的最小数字要大,那么将堆中的最小数字去掉,并加入刚写入的数字。
具体写了三种方法:

方法一:最小堆

#include <algorithm>
#include <iostream>
using namespace std;

struct min_Heap  
{  
    int array[1000002], size;

    void siftDown ( int start ) //下滑调整,若子女的值小于父节点的值,则子节点上浮,之后继续向下层比较  
    {  
        int i = start, j = i * 2;  
        int temp = array[start];  
        while ( j <= size )  
        {  
            if ( j < size && array[j] > array[j+1] ) ++j;  
            if ( temp <= array[j] ) break;  
            else { swap(array[i],array[j]); i = j; j = j * 2; }  
        }  
    }
	
    void siftUp ( int start ) //上滑调整,若子女的值小于父节点的值则互相交换  
    {  
        int j = start, i = j / 2;  
        int temp = array[j];  
        while ( j >= 1 )  
        {  
            if ( temp >= array[i] ) break;  
            else { swap(array[i],array[j]); j = i; i = i / 2; }  
        }  
    }
	
    void build_minHeap () //建堆  
    {  
        int current = size / 2;  
        while ( current >= 1 )  
        {  
            siftDown ( current );  
            current--;  
        }  
    }
	
} minHeap; 

int main()
{
	char oper[3];
	int n, k, num;
	while ( scanf("%d%d",&n,&k) != EOF )
	{
		minHeap.size = 0;

		for ( int i = 1; i <= n; i++ )
		{
			scanf("%s",oper);
			if ( oper[0] == 'I' )
			{
				scanf("%d",&num);
				if ( minHeap.size < k )
				{
					minHeap.size++;
					minHeap.array[minHeap.size] = num;

					if ( minHeap.size == k )
					    minHeap.build_minHeap ();
				}
				else if ( num > minHeap.array[1] )
				{
					minHeap.array[1] = num;
					minHeap.siftDown ( 1 );
				}
			}
			else
				printf("%d\n",minHeap.array[1]);
		}
	}
	return 0;
}


方法二:优先级队列。其实和上面一种方法没有本质区别
#include <queue>
#include <iostream>
using namespace std;

int main()
{
	char oper[3];
	int n, k, num;
	while ( scanf("%d%d",&n,&k) != EOF )
	{
		priority_queue< int,vector<int>,greater<int> > que;

		for ( int i = 1; i <= n; i++ )
		{
			scanf("%s",oper);
			if ( oper[0] == 'I' )
			{
				scanf("%d",&num);
				if ( que.size() < k )
					que.push(num);
				else
				{
					if ( num > que.top() )
					{
						que.pop();
						que.push(num);
					}
				}
			}
			else
				printf("%d\n",que.top() );
		}
	}
	return 0;
}


方法三:线段树实现。线段树的实现效率虽然比以上两种略差,但是实用性很强。更容易拓展。

#include <algorithm>
#include <iostream>
using namespace std;

#define L(u) ( u << 1 )
#define R(u) ( u << 1 | 1 )
#define N 1000001

struct item
{
	int l, r, v;
} node[N*3];


void build ( int u, int l, int r )
{
	node[u].l = l;
	node[u].r = r;
	node[u].v = 0;
	if ( l == r ) return;
	int mid = ( l + r ) >> 1;
	build ( L(u), l, mid );
	build ( R(u), mid+1, r);
}

void update ( int u, int pos )
{
	node[u].v++;
	if ( node[u].l == pos && node[u].r == pos )
		return;
	int mid = ( node[u].l + node[u].r ) >> 1;
	if ( pos <= mid )
		update ( L(u), pos );
	else
		update ( R(u), pos );
}

int query ( int u, int k )
{
	if ( node[u].l == node[u].r )
		return node[u].l;
	if ( k <= node[L(u)].v )
		return query ( L(u), k );
	else
		return query ( R(u), k - node[L(u)].v );
}

int main()
{
	char oper[3];
	int n, k, num, cnt;
	while ( scanf("%d%d",&n,&k) != EOF )
	{
		cnt = 0;
		build ( 1, 1, 1000000 );
		for ( int i = 1; i <= n; i++ )
		{
			scanf("%s",oper);
			if ( oper[0] == 'I' )
			{
				scanf("%d",&num);
				cnt++; /* cnt 统计一共写下的数字 */
				update ( 1, num );
			}
			else printf("%d\n", query(1,cnt-k+1) ); /* 注意是 cnt-k+1 */
		}
	}
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值