您的位置:首页 > 理论基础 > 数据结构算法

有关堆存取的习题两例:寻找大富翁、Windows消息队列

2018-01-13 17:26 381 查看
关于堆 (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消息队列”的题面如下:

4000
消息队列是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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息