关于堆 (heap) 的笔记见 -> 有关堆 (数据结构) 的笔记
“寻找大富翁”的题面是这样:
2015年胡润研究院的调查显示,截至2014年9月,个人资产在600万元以上高净值人群达290万人。假设给出N个人的个人资产值,请快速找出资产排前M位的大富翁。
输入格式:
输入首先给出两个正整数N(≤10^6)和M(≤10),其中N为总人数,M为需要找出的大富翁数;接下来一行给出N个人的个人资产值,以百万元为单位,为不超过长整型范围的整数。数字间以空格分隔。
输出格式:
在一行内按非递增顺序输出资产排前M位的大富翁的个人资产值。数字间以空格分隔,但结尾不得有多余空格。
输入样例:
8 3
8 12 7 3 20 9 5 18输出样例:
20 18 12
“从若干数里找最大的x个数”这类的题目可以通过建立堆解出,下面是解法。
#include<stdio.h>
#include<stdlib.h>
#define MINVAL -1
#define EMPTY -404
struct node
{
int* body;
int size;
int capacity;
};
typedef struct node* Heap;
//这个命名可能是迷惑性的,因为我也用这个结构体建了一个栈
Heap NewHeap(int size);
void DelKit(Heap heap);
int EnHeap(Heap heap, int tmp);
int PopHeap(Heap heap);
int EnStack(Heap stack, int tmp);
int PopStack(Heap stack);
int main(void)
{
int N, M;
scanf("%d %d", &N, &M);
getchar();
Heap H = NewHeap(M);
Heap stack = NewHeap(M);
int i, tmp;
for(i = 0; i < N; i++)
{
scanf("%d", &tmp);
getchar();
while(EnHeap(H, tmp))
PopHeap(H);
}
int sizeH = H->size;
for(i = 0; i < sizeH; i++)
{
tmp = PopHeap(H);
EnStack(stack, tmp);
}
i = 0;
while(stack->size)
{
tmp = PopStack(stack);
if(i != 0)
putchar(' ');
printf("%d", tmp);
i++;
}
DelKit(H);
DelKit(stack);
return 0;
}
Heap NewHeap(int size)
{
Heap heap = (Heap)calloc(1, sizeof(struct node));
heap->body = (int*)calloc(size+1, sizeof(int));
heap->body[0] = MINVAL;
heap->size = 0;
heap->capacity = size;
return heap;
}
void DelKit(Heap heap)
{
free(heap->body);
heap->body = NULL;
free(heap);
heap = NULL;
return;
}
//插入数据到小顶堆
int EnHeap(Heap heap, int tmp)
{
int x;
if(heap->size == heap->capacity)
{
return 1;
}
x = ++heap->size;
for(; heap->body[x>>1]>tmp && x>0; x>>=1) //循环限定 x>=1, 可以不使用哨兵(body[0])
heap->body[x] = heap->body[x>>1];
heap->body[x] = tmp;
return 0;
}
//从小顶堆弹出
int PopHeap(Heap heap)
{
int Parent, Child;
int minData, x;
if(heap->size == 0)
{
return EMPTY;
}
minData = heap->body[1];
x = heap->body[heap->size--];
for(Parent = 1; Parent<<1 <= heap->size; Parent = Child)
{
Child = Parent<<1;
if( (Child < heap->size) && (heap->body[Child] > heap->body[Child+1]) )
Child++;
if(x <= heap->body[Child])
break;
else
heap->body[Parent] = heap->body[Child];
}
heap->body[Parent] = x;
return minData;
}
//进栈
int EnStack(Heap stack, int tmp)
{
if(stack->size == stack->capacity)
{
return 1;
}
stack->body[stack->size++] = tmp;
return 0;
}
//出栈
int PopStack(Heap stack)
{
if(stack->size == 0)
{
return EMPTY;
}
stack->size--;
return stack->body[stack->size];
}
“Windows消息队列”的题面如下:
消息队列是Windows系统的基础。对于每个进程,系统维护一个消息队列。如果在进程中有特定事件发生,如点击鼠标、文字改变等,系统将把这个消息加到队列当中。同时,如果队列不是空的,这一进程循环地从队列中按照优先级获取消息。请注意优先级值低意味着优先级高。
请编辑程序模拟消息队列,将消息加到队列中以及从队列中获取消息。输入格式:
输入首先给出正整数N(≤10^5),随后N行,每行给出一个指令——GET或PUT,分别表示从队列中取出消息或将消息添加到队列中。 如果指令是PUT,后面就有一个消息名称、以及一个正整数表示消息的优先级,此数越小表示优先级越高。
消息名称是长度不超过10个字符且不含空格的字符串;题目保证队列中消息的优先级无重复,且输入至少有一个GET。输出格式:
对于每个GET指令,在一行中输出消息队列中优先级最高的消息的名称和参数。如果消息队列中没有消息,输出EMPTY
QUEUE!。对于PUT指令则没有输出。输入样例:
9
PUT msg1 5
PUT msg2 4
GET
PUT msg3 2
PUT msg4 4
GET
GET
GET输出样例:
msg2
msg3
msg4
msg1
EMPTY QUEUE!
因为要靠结构体内的成员作为标签定义大小关系,会稍微有些麻烦。但涉及到频繁存取数据的场合使用堆依然是合适的。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#define MINDATA -1
typedef char wMSG[11];
struct worder
{
wMSG msg;
int rank;
};
typedef struct worder* ordTbl;
struct orderHeap
{
ordTbl body;
int heapSize;
int Capacity;
};
typedef struct orderHeap* Ord;
Ord OrderTable(int);
void DelOrderTable(Ord);
bool EnOrderTable(Ord, wMSG, int);
void GetOrderTable(Ord);
int main()
{
int size;
scanf("%d", &size);
getchar();
Ord msgQue = OrderTable(size+1);
char ord[4];
wMSG msgTmp;
int i, rankTmp;
for(i=0; i<size; i++)
{
scanf("%s", &ord);
getchar();
if(ord[0] == 'P') //Put in
{
scanf("%s %d", &msgTmp, &rankTmp);
getchar();
EnOrderTable(msgQue, msgTmp, rankTmp);
}
else //Get
{
GetOrderTable(msgQue);
}
}
DelOrderTable(msgQue);
return 0;
}
Ord OrderTable(int size)
{
Ord WOrd = (Ord)calloc(1,sizeof(struct orderHeap));
WOrd->body = (ordTbl)calloc(size, sizeof(struct worder));
WOrd->Capacity = size;
WOrd->heapSize = 0;
WOrd->body[0].rank = MINDATA;
return WOrd;
}
void DelOrderTable(Ord WOrd)
{
free(WOrd->body);
WOrd->body = NULL;
free(WOrd);
WOrd = NULL;
return;
}
//向堆中写入新节点, r 是 rank
bool EnOrderTable(Ord WOrd, wMSG ch, int r)
{
if(WOrd->heapSize == WOrd->Capacity)
{
return false;
}
int i = ++WOrd->heapSize;
for(; WOrd->body[i>>1].rank > r; i>>=1)
WOrd->body[i] = WOrd->body[i>>1];
WOrd->body[i].rank = r;
strcpy(WOrd->body[i].msg, ch);
return true;
}
void GetOrderTable(Ord WOrd)
{
if(WOrd->heapSize == 0)
{
printf("EMPTY QUEUE!\n");
return;
}
int Parent, Child;
ordTbl tempNode;
//堆顶元素在下滤后会被覆盖,在这之前显示信息
printf("%s\n", WOrd->body[1].msg);
//下滤节点在调整前的堆区域外,可直接取地址
tempNode = &WOrd->body[WOrd->heapSize--];
for(Parent = 1; Parent<<1 <= WOrd->heapSize; Parent = Child)
{
Child = Parent<<1;
if( (Child != WOrd->heapSize)
&& (WOrd->body[Child].rank > WOrd->body[Child+1].rank) )
Child++;
if( tempNode->rank <= WOrd->body[Child].rank )
break;
else
WOrd->body[Parent] = WOrd->body[Child]; //下滤
}
WOrd->body[Parent] = *tempNode; //下滤后将节点放入
return;
}