7-7 Windows消息队列 (25 分)
消息队列是Windows系统的基础。对于每个进程,系统维护一个消息队列。如果在进程中有特定事件发生,如点击鼠标、文字改变等,系统将把这个消息加到队列当中。同时,如果队列不是空的,这一进程循环地从队列中按照优先级获取消息。请注意优先级值低意味着优先级高。请编辑程序模拟消息队列,将消息加到队列中以及从队列中获取消息。
输入格式:
输入首先给出正整数N(≤105),随后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
GET
输出样例:
msg2
msg3
msg4
msg1
EMPTY QUEUE!
思路:
最小堆的运用问题,先直接照搬了教材(《数据结构》浙大陈越版)的标程,再创建结构体数组存消息和权值,权值作为元素进行最小堆的增删改查即可。遇到GET就取出最小的权值,再找到最小权值对应的消息输出。需要注意可能有重复权值对应不同的消息,只是它们存在于不同的时刻而已,所以输出后需要更新权值为一个取不到的值(-1)。码代码的时候就想到有可能会在大规模数据下超时,交一发果然超了,毕竟由权值找消息的操作是遍历结构体数组,太费时间。(以下是超时代码)
超时代码
#include<stdio.h>
#include<malloc.h>
#include<string.h>
#define MINDATA 0
#define ERROR -1
typedef int ElementType;
typedef struct HNode * Heap;
struct HNode
{
ElementType *Data; //存储元素的数组
int size; //堆中当前元素个数
int Capacity; //堆的最大容量
};
typedef Heap MinHeap; //最小堆
MinHeap CreatHeap(int MaxSize)
{ //创建容量为MaxSize的空的最小堆
MinHeap H = (MinHeap)malloc(sizeof(struct HNode));
H -> Data = (ElementType *)malloc((MaxSize + 1) * sizeof(ElementType));
H -> size = 0;
H -> Capacity = MaxSize;
H -> Data[0] = MINDATA; //定义“哨兵”为最小堆中所有可能元素的值
return H;
}
bool IsFull(MinHeap H)
{
return (H -> size == H -> Capacity);
}
bool Insert(MinHeap H, ElementType X)
{
int i;
if(IsFull(H))
{
printf("最大堆已满\n");
return false;
}
i = ++ H -> size; //新增结点
for( ; H -> Data[i/2] > X; i/=2)
{
H -> Data[i] = H -> Data[i/2]; //上滤X
}
H -> Data[i] = X;
return true;
}
bool IsEmpty(MinHeap H)
{
return (H -> size == 0);
}
ElementType DeleteMin(MinHeap H)
{ //从最小堆H中取出键值为最大的元素,并删除一个结点(重点在安置最后一个结点)
int Parent, Child;
ElementType MinItem, X;
if(IsEmpty(H))
{
printf("EMPTY QUEUE!\n");
return ERROR;
}
MinItem = H -> Data[1]; //取出根结点
//用最小堆中最后一个元素从根结点开始向上过滤下层结点
X = H -> Data[H -> size--]; //注意当前堆的规模要减小
for(Parent = 1; Parent*2 <= H->size; Parent = Child)
{
Child = Parent * 2;
if((Child != H -> size) && (H -> Data[Child] > H -> Data[Child+1]))
Child++; //Child指向左右子结点中的较小者
if(X <= H -> Data[Child])
break; //找到合适的位置
else
H -> Data[Parent] = H -> Data[Child];
}
H -> Data[Parent] = X;
return MinItem;
}
int main(int argc, char const *argv[])
{
int N;
int i = 0, j = 0;
char order[5];
struct message
{
char str[12];
int weight;
}msg[100000];
scanf("%d", &N);
MinHeap H = CreatHeap(N);
for(i=0; i<N; i++)
{
scanf("%s", order);
if(!strcmp(order, "PUT"))
{
scanf("%s %d", msg[j].str, &msg[j].weight);
Insert(H, msg[j].weight);
j++;
}
else if(!strcmp(order, "GET"))
{
ElementType result;
result = DeleteMin(H);
if(result != ERROR)
{
int k;
for(k=0; k<=j; k++)
{
if(msg[k].weight == result)
{
printf("%s\n", msg[k].str);
msg[k].weight = -1;
break;
}
}
}
}
}
return 0;
}
改进:
在网上找了很多大佬的代码还是不得解(不会C++ STL哭晕在厕所)。注意到题目保证队列中消息的优先级(权值)无重复,于是可以在结构体数组下标上下手,这样就不用在Insert()和DeleteMin()函数中加参数来存权值对应消息内容或者是别的方法了(想想就麻烦)。做法:把结构体数组的成员改成char* str并且去掉权值weight,然后以权值weight作为结构体数组下标即可。这样一来msg[weight].str就对应着相应信息,直接输出就好了。
依旧存在的问题:
现在的代码能过了,但是由于输入时遇到GET就输出了,输出形式和实例不一样(巨丑),懒得改了。。。
AC代码
#include<stdio.h>
#include<malloc.h>
#include<string.h>
#define MINDATA 0
#define ERROR -1
typedef int ElementType;
typedef struct HNode * Heap;
struct HNode
{
ElementType *Data; //存储元素的数组
int size; //堆中当前元素个数
int Capacity; //堆的最大容量
};
typedef Heap MinHeap; //最小堆
MinHeap CreatHeap(int MaxSize)
{ //创建容量为MaxSize的空的最小堆
MinHeap H = (MinHeap)malloc(sizeof(struct HNode));
H -> Data = (ElementType *)malloc((MaxSize + 1) * sizeof(ElementType));
H -> size = 0;
H -> Capacity = MaxSize;
H -> Data[0] = MINDATA; //定义“哨兵”为最小堆中所有可能元素的值
return H;
}
bool IsFull(MinHeap H)
{
return (H -> size == H -> Capacity);
}
bool Insert(MinHeap H, ElementType X)
{
int i;
if(IsFull(H))
{
printf("最大堆已满\n");
return false;
}
i = ++ H -> size; //新增结点
for( ; H -> Data[i/2] > X; i/=2)
{
H -> Data[i] = H -> Data[i/2]; //上滤X
}
H -> Data[i] = X;
return true;
}
bool IsEmpty(MinHeap H)
{
return (H -> size == 0);
}
ElementType DeleteMin(MinHeap H)
{ //从最小堆H中取出键值为最大的元素,并删除一个结点(重点在安置最后一个结点)
int Parent, Child;
ElementType MinItem, X;
if(IsEmpty(H))
{
printf("EMPTY QUEUE!\n");
return ERROR;
}
MinItem = H -> Data[1]; //取出根结点
//用最小堆中最后一个元素从根结点开始向上过滤下层结点
X = H -> Data[H -> size--]; //注意当前堆的规模要减小
for(Parent = 1; Parent*2 <= H->size; Parent = Child)
{
Child = Parent * 2;
if((Child != H -> size) && (H -> Data[Child] > H -> Data[Child+1]))
Child++; //Child指向左右子结点中的较小者
if(X <= H -> Data[Child])
break; //找到合适的位置
else
H -> Data[Parent] = H -> Data[Child];
}
H -> Data[Parent] = X;
return MinItem;
}
int main(int argc, char const *argv[])
{
int N;
int i = 0, j = 0;
char order[5];
struct message
{
char *str;
}msg[100002];
scanf("%d", &N);
MinHeap H = CreatHeap(N);
for(i=0; i<N; i++)
{
scanf("%s", order);
if(!strcmp(order, "PUT"))
{
char temp[12];
int weight;
scanf("%s %d", temp, &weight); //数据暂存
msg[weight].str = (char*)malloc(sizeof(char)*12); //给str分配空间
msg[weight].str = strcpy(msg[weight].str, temp); //字符串赋值(返回char*类型)
Insert(H, weight);
}
else if(!strcmp(order, "GET"))
{
ElementType result;
result = DeleteMin(H);
if(result != ERROR)
{
printf("%s\n", msg[result].str);
}
}
}
return 0;
}