2025 A卷 100分 题型
本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析;
并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式!
华为OD机试真题《模拟消息队列》:
题目名称:模拟消息队列
属性 | 内容 |
---|---|
知识点 | 事件排序、优先级处理、逻辑处理 |
时间限制 | 1秒 |
空间限制 | 256MB |
限定语言 | 不限 |
题目描述
模拟一个消息队列的运作,包含一个发布者和若干消费者。发布者在特定时刻发送消息,若此时有消费者订阅,消息将发送给优先级最高的消费者(消费者按输入顺序升序排列,升序即优先级递增)。若没有订阅者,消息被丢弃。消费者在特定时刻订阅或取消订阅,同一时刻的事件处理顺序如下:
- 订阅操作优先于消息发送
- 取消订阅优先于消息发送
输入描述
输入为两行:
- 第一行:2N个正整数,表示N条消息的发送时刻和内容(时刻不重复,但未按时间排序)。例如:
2 22 1 11 4 44
代表3条消息,时刻分别为2(内容22)、1(内容11)、4(内容44)。 - 第二行:2M个正整数,表示M个消费者的订阅和取消订阅时刻。例如:
1 7 2 3
代表两个消费者,第一个订阅时刻1、取消时刻7;第二个订阅时刻2、取消时刻3。
输出描述
输出M行,每行为对应消费者收到的消息内容(按接收顺序排列),若未收到消息则输出-1
。
测试用例
输入
2 22 1 11 4 44 5 55 3 33 1 7 2 3
输出
11 33 44 55 22
说明:消息在时刻1、2、3、4、5依次处理,优先级高的消费者(后订阅的)优先接收消息。
输入
5 64 11 64 9 97 9 11 4 9
输出
97 64
Java
问题分析
我们需要模拟消息队列的工作流程,其中发布者在特定时刻发送消息,消费者在特定时刻订阅或取消订阅。消息发送时,优先发送给当前活跃且优先级最高的消费者。优先级由消费者的输入顺序决定,后订阅的消费者优先级更高。
解决思路
- 事件收集与排序:将所有订阅、取消订阅、消息发送事件按时间排序,同一时刻的事件按订阅 → 取消 → 消息的顺序处理。
- 维护活跃消费者:使用
TreeSet
维护当前活跃的消费者,按优先级(输入顺序的逆序)排序。 - 消息分发:处理消息事件时,将消息发送给当前优先级最高的消费者。
Java 代码实现
import java.util.*;
public class Main {
static class Event implements Comparable<Event> {
int time; // 事件发生时间
int type; // 0:订阅, 1:取消订阅, 2:消息
int consumerIdx; // 消费者索引(用于订阅/取消)
int message; // 消息内容(仅消息事件有效)
public Event(int time, int type, int consumerIdx, int message) {
this.time = time;
this.type = type;
this.consumerIdx = consumerIdx;
this.message = message;
}
@Override
public int compareTo(Event other) {
if (this.time != other.time) {
return Integer.compare(this.time, other.time);
} else {
return Integer.compare(this.type, other.type); // 订阅→取消→消息
}
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 解析消息输入
String[] msgInput = scanner.nextLine().split(" ");
List<Event> events = new ArrayList<>();
for (int i = 0; i < msgInput.length; i += 2) {
int time = Integer.parseInt(msgInput[i]);
int msg = Integer.parseInt(msgInput[i + 1]);
events.add(new Event(time, 2, -1, msg)); // 类型2是消息事件
}
// 解析消费者输入
String[] consumerInput = scanner.nextLine().split(" ");
int m = consumerInput.length / 2;
List<List<Integer>> consumerMsgs = new ArrayList<>();
for (int i = 0; i < m; i++) {
int subTime = Integer.parseInt(consumerInput[2 * i]);
int unsubTime = Integer.parseInt(consumerInput[2 * i + 1]);
events.add(new Event(subTime, 0, i, -1)); // 订阅事件
events.add(new Event(unsubTime, 1, i, -1)); // 取消事件
consumerMsgs.add(new ArrayList<>()); // 初始化消费者的消息列表
}
// 按时间排序所有事件
Collections.sort(events);
// 维护当前活跃的消费者(按优先级降序排列)
TreeSet<Integer> activeConsumers = new TreeSet<>(Collections.reverseOrder());
// 处理所有事件
for (Event event : events) {
switch (event.type) {
case 0: // 订阅事件
activeConsumers.add(event.consumerIdx);
break;
case 1: // 取消订阅事件
activeConsumers.remove(event.consumerIdx);
break;
case 2: // 消息事件
if (!activeConsumers.isEmpty()) {
int consumer = activeConsumers.first(); // 最高优先级的消费者
consumerMsgs.get(consumer).add(event.message);
}
break;
}
}
// 输出结果
for (List<Integer> msgs : consumerMsgs) {
if (msgs.isEmpty()) {
System.out.println(-1);
} else {
System.out.println(
String.join(" ", msgs.stream().map(String::valueOf).toArray(String[]::new))
);
}
}
}
}
代码解析
Event 类:
static class Event implements Comparable<Event> { int time; // 事件时间 int type; // 0:订阅, 1:取消, 2:消息 int consumerIdx; // 消费者索引 int message; // 消息内容 }
- 封装事件的时间、类型、消费者索引和消息内容。
事件排序:
@Override public int compareTo(Event other) { if (this.time != other.time) return time比较结果; else return type比较结果; // 同一时间,订阅→取消→消息 }
- 按时间升序排序,同一时间的事件按订阅 → 取消 → 消息的顺序处理。
解析输入:
String[] msgInput = scanner.nextLine().split(" "); for (int i = 0; i < msgInput.length; i += 2) { int time = Integer.parseInt(msgInput[i]); int msg = Integer.parseInt(msgInput[i + 1]); events.add(new Event(time, 2, -1, msg)); // 消息事件 }
- 将输入拆分为消息时间和内容,生成消息事件。
处理消费者事件:
String[] consumerInput = scanner.nextLine().split(" "); for (int i = 0; i < m; i++) { int subTime = Integer.parseInt(consumerInput[2*i]); int unsubTime = Integer.parseInt(consumerInput[2*i+1]); events.add(new Event(subTime, 0, i, -1)); // 订阅事件 events.add(new Event(unsubTime, 1, i, -1)); // 取消事件 }
- 解析每个消费者的订阅和取消时间,生成对应事件。
维护活跃消费者:
TreeSet<Integer> activeConsumers = new TreeSet<>(Collections.reverseOrder());
- 使用
TreeSet
并按逆序排序,确保活跃消费者中优先级最高的(索引最大)排在前面。
- 使用
处理消息事件:
if (!activeConsumers.isEmpty()) { int consumer = activeConsumers.first(); // 最高优先级消费者 consumerMsgs.get(consumer).add(event.message); }
- 当前有活跃消费者时,将消息发送给优先级最高的消费者。
示例测试
示例1:
输入:
2 22 1 11 4 44 5 55 3 33
1 7 2 3
输出:
11 33 44 55
22
解析:
- 时刻1:消费者0订阅,发送消息11 → 消费者0接收。
- 时刻2:消费者1订阅,发送消息22 → 消费者1接收。
- 时刻3:消费者1取消,发送消息33 → 消费者0接收。
- 时刻4和5:发送消息44和55 → 消费者0接收。
示例2:
输入:
5 64 11 64 9 97
9 11 4 9
输出:
97
64
解析:
- 时刻5:消息64发送时只有消费者1活跃 → 消费者1接收。
- 时刻9:消费者0订阅,消费者1取消,发送消息97 → 消费者0接收。
综合分析
时间复杂度:
- 事件排序:
O(E log E)
,其中E
是事件总数(消息 + 订阅 + 取消)。 - 处理事件:
O(E log M)
,M
是消费者数量,TreeSet
操作复杂度为O(log M)
。 - 总体复杂度
O(E log E + E log M)
,满足题目要求。
- 事件排序:
空间复杂度:
- 存储事件和消费者消息列表:
O(E + M)
,符合空间限制。
- 存储事件和消费者消息列表:
最优性:
- 使用
TreeSet
高效维护活跃消费者,确保每次操作时间复杂度为O(log M)
。 - 事件排序确保处理顺序符合题目要求,保证逻辑正确性。
- 使用
适用性:
- 处理大规模数据(消费者数 ≤ 1e4,消息数 ≤ 1e4)时依然高效。
- 代码结构清晰,易于维护和扩展。
python
问题分析
我们需要模拟一个消息队列,其中包含发布者和多个消费者。发布者在特定时间发送消息,消费者在特定时间订阅或取消订阅。当消息发送时,如果有活跃的消费者,消息将发送给优先级最高的消费者(按输入顺序升序排列,后订阅的优先级更高)。输出每个消费者接收的消息,按接收顺序排列。
解决思路
- 事件收集与排序:将所有事件(订阅、取消、消息)按时间排序,同一时间的事件按订阅 → 取消 → 消息的顺序处理。
- 维护活跃消费者:使用集合保存当前活跃的消费者索引。
- 消息分发:处理消息时,选择优先级最高的活跃消费者(索引最大的消费者)。
Python 代码实现
def main():
import sys
# 读取输入
msg_line = sys.stdin.readline().strip()
consumer_line = sys.stdin.readline().strip()
# 解析消息输入
msg_parts = list(map(int, msg_line.split()))
events = []
for i in range(0, len(msg_parts), 2):
time = msg_parts[i]
content = msg_parts[i+1]
events.append((time, 2, -1, content)) # 类型2表示消息
# 解析消费者输入
consumer_parts = list(map(int, consumer_line.split()))
m = len(consumer_parts) // 2
consumers = [[] for _ in range(m)] # 存储每个消费者的消息
for i in range(m):
sub_time = consumer_parts[2*i]
unsub_time = consumer_parts[2*i+1]
events.append((sub_time, 0, i, -1)) # 类型0表示订阅
events.append((unsub_time, 1, i, -1)) # 类型1表示取消
# 排序事件:时间升序,同时间按类型升序(0 < 1 < 2)
events.sort(key=lambda x: (x[0], x[1]))
active_consumers = set() # 当前活跃的消费者索引
# 处理所有事件
for event in events:
time, typ, consumer_idx, content = event
if typ == 0: # 订阅
active_consumers.add(consumer_idx)
elif typ == 1: # 取消
if consumer_idx in active_consumers:
active_consumers.remove(consumer_idx)
elif typ == 2: # 消息
if active_consumers:
# 选择优先级最高的消费者(索引最大的)
max_consumer = max(active_consumers)
consumers[max_consumer].append(content)
# 输出结果
for msgs in consumers:
if not msgs:
print(-1)
else:
print(" ".join(map(str, msgs)))
if __name__ == "__main__":
main()
代码解析
输入解析:
msg_parts = list(map(int, msg_line.split())) for i in range(0, len(msg_parts), 2): events.append((time, 2, -1, content))
- 将消息时间和内容解析为事件,类型为2(消息事件)。
消费者事件处理:
for i in range(m): sub_time = consumer_parts[2*i] events.append((sub_time, 0, i, -1)) # 订阅事件 events.append((unsub_time, 1, i, -1)) # 取消事件
- 解析每个消费者的订阅和取消时间,生成对应事件。
事件排序:
events.sort(key=lambda x: (x[0], x[1]))
- 按时间升序排序,同一时间的事件按类型排序(订阅→取消→消息)。
维护活跃消费者:
active_consumers = set() if typ == 0: active_consumers.add(consumer_idx) elif typ == 1: active_consumers.discard(consumer_idx)
- 订阅时添加消费者索引,取消时移除。
消息分发:
if active_consumers: max_consumer = max(active_consumers) consumers[max_consumer].append(content)
- 当前有活跃消费者时,选择索引最大的(优先级最高)接收消息。
示例测试
示例1:
输入:
2 22 1 11 4 44 5 55 3 33
1 7 2 3
输出:
11 33 44 55
22
解析:
- 时间1:消费者0订阅,发送消息11 → 消费者0接收。
- 时间2:消费者1订阅,发送消息22 → 消费者1接收。
- 时间3:消费者1取消,发送消息33 → 消费者0接收。
- 时间4和5:发送消息44和55 → 消费者0接收。
示例2:
输入:
5 64 11 64 9 97
9 11 4 9
输出:
97
64
解析:
- 时间5:消息64发送时只有消费者1活跃 → 消费者1接收。
- 时间9:消费者0订阅,消费者1取消,发送消息97 → 消费者0接收。
综合分析
时间复杂度:
- 事件排序:
O(E log E)
,E
是事件总数(消息 + 订阅 + 取消)。 - 处理事件:
O(E)
,每次操作集合的时间复杂度为平均O(1)
。 - 总体复杂度
O(E log E)
,满足题目要求。
- 事件排序:
空间复杂度:
- 存储事件和消费者消息列表:
O(E + M)
,符合空间限制。
- 存储事件和消费者消息列表:
正确性保障:
- 事件排序确保处理顺序符合题目要求。
- 使用集合维护活跃消费者,快速找到优先级最高的消费者。
优势:
- 代码简洁,逻辑清晰。
- 使用集合高效处理消费者状态的增删。
- 严格按题目要求处理事件顺序。
JavaScript
问题分析
我们需要模拟一个消息队列系统,其中包含消息发布者和多个消费者。核心要求如下:
事件触发规则:
- 发布者在特定时刻发送消息
- 消费者在特定时刻订阅或取消订阅
- 同一时刻的事件处理顺序:订阅 > 取消订阅 > 消息发送
消息分发规则:
- 消息发送时,如果存在活跃消费者,发送给优先级最高的消费者(最后订阅的)
- 没有消费者时消息丢弃
输出要求:
- 每个消费者收到的消息按接收顺序排列
- 未收到消息时输出
-1
解决思路
统一事件处理:
- 将所有操作抽象为事件(订阅、取消订阅、消息)
- 收集所有事件到同一队列
事件排序:
- 按时间升序排列
- 同一时间的事件按类型排序(订阅→取消→消息)
消费者管理:
- 使用数组维护活跃消费者列表
- 订阅时加入列表末尾(保证最后订阅的优先级最高)
- 取消订阅时从列表中移除
消息分发:
- 处理消息事件时,选择优先级最高的消费者(列表最后一个)
JavaScript 代码实现
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let inputLines = [];
rl.on('line', (line) => {
inputLines.push(line.trim());
if (inputLines.length === 2) processInput();
});
function processInput() {
// ================= 输入解析阶段 =================
// 解析消息数据(时间, 内容交替出现)
const messageEvents = [];
const msgParts = inputLines[0].split(' ').map(Number);
for (let i = 0; i < msgParts.length; i += 2) {
messageEvents.push({
time: msgParts[i],
type: 'MESSAGE', // 消息类型标识
content: msgParts[i + 1]
});
}
// 解析消费者数据(订阅时间, 取消时间交替出现)
const consumerEvents = [];
const consumerParts = inputLines[1].split(' ').map(Number);
const consumers = Array.from({ length: consumerParts.length / 2 }, () => []);
for (let i = 0; i < consumerParts.length; i += 2) {
const subTime = consumerParts[i];
const unsubTime = consumerParts[i + 1];
// 添加订阅事件
consumerEvents.push({
time: subTime,
type: 'SUBSCRIBE', // 订阅类型标识
consumerIdx: i / 2 // 消费者索引
});
// 添加取消订阅事件
consumerEvents.push({
time: unsubTime,
type: 'UNSUBSCRIBE', // 取消订阅类型标识
consumerIdx: i / 2
});
}
// ================= 事件处理阶段 =================
// 合并所有事件并排序
const allEvents = [...messageEvents, ...consumerEvents];
// 自定义排序规则
allEvents.sort((a, b) => {
if (a.time !== b.time) {
return a.time - b.time; // 时间升序
}
// 同一时间的事件类型优先级
const typeOrder = { SUBSCRIBE: 0, UNSUBSCRIBE: 1, MESSAGE: 2 };
return typeOrder[a.type] - typeOrder[b.type];
});
// 维护活跃消费者列表
const activeConsumers = [];
// 处理每个事件
for (const event of allEvents) {
switch (event.type) {
case 'SUBSCRIBE':
// 将消费者添加到列表末尾(保证最后订阅的优先级最高)
activeConsumers.push(event.consumerIdx);
break;
case 'UNSUBSCRIBE':
// 从列表中移除消费者
const index = activeConsumers.indexOf(event.consumerIdx);
if (index !== -1) {
activeConsumers.splice(index, 1);
}
break;
case 'MESSAGE':
// 分发消息给最高优先级消费者
if (activeConsumers.length > 0) {
const targetConsumer = activeConsumers[activeConsumers.length - 1];
consumers[targetConsumer].push(event.content);
}
break;
}
}
// ================= 结果输出阶段 =================
consumers.forEach(consumer => {
console.log(consumer.length > 0 ? consumer.join(' ') : '-1');
});
}
代码解析
1. 输入解析
// 解析消息数据
const msgParts = inputLines[0].split(' ').map(Number);
for (let i = 0; i < msgParts.length; i += 2) {
messageEvents.push({
time: msgParts[i],
type: 'MESSAGE',
content: msgParts[i + 1]
});
}
// 解析消费者数据
const consumerParts = inputLines[1].split(' ').map(Number);
const consumers = Array.from({ length: consumerParts.length / 2 }, () => []);
for (let i = 0; i < consumerParts.length; i += 2) {
// 生成订阅和取消事件
}
- 将原始输入转换为结构化事件对象
- 每个消费者初始化一个空消息列表
2. 事件排序
allEvents.sort((a, b) => {
if (a.time !== b.time) return a.time - b.time;
const typeOrder = { SUBSCRIBE: 0, UNSUBSCRIBE: 1, MESSAGE: 2 };
return typeOrder[a.type] - typeOrder[b.type];
});
- 优先按时间升序排列
- 同一时间的事件按类型优先级排序
3. 消费者管理
case 'SUBSCRIBE':
activeConsumers.push(event.consumerIdx);
break;
case 'UNSUBSCRIBE':
const index = activeConsumers.indexOf(event.consumerIdx);
if (index !== -1) {
activeConsumers.splice(index, 1);
}
break;
- 订阅时添加到数组末尾
- 取消时查找并移除元素
4. 消息分发
case 'MESSAGE':
if (activeConsumers.length > 0) {
const targetConsumer = activeConsumers[activeConsumers.length - 1];
consumers[targetConsumer].push(event.content);
}
break;
- 选择数组最后一个元素(最后订阅的)
- 将消息存入对应消费者的消息列表
测试用例
用例1:
输入:
2 22 1 11 4 44 5 55 3 33
1 7 2 3
输出:
11 33 44 55
22
流程说明:
- 时间1:消费者0订阅 → 处理消息11
- 时间2:消费者1订阅 → 处理消息22
- 时间3:消费者1取消 → 处理消息33(发给消费者0)
- 时间4和5:消息44和55发给消费者0
用例2:
输入:
5 64 11 64 9 97
9 11 4 9
输出:
97
64
综合分析
时间复杂度:
- 事件排序:O(n log n)
- 事件处理:O(n * m)(m为消费者数量)
- 满足题目要求的性能指标
空间复杂度:
- 存储事件和消费者消息列表:O(n + m)
关键优势:
- 统一的事件处理模型
- 清晰的优先级管理逻辑
- 易于扩展的事件类型支持
C++
问题分析
我们需要模拟一个消息队列,其中发布者在不同时刻发送消息,消费者在特定时刻订阅或取消订阅。消息发送时,优先发送给当前活跃且优先级最高的消费者(后订阅的优先级更高)。输出每个消费者接收的消息列表。
解决思路
- 事件收集与排序:将所有事件(订阅、取消、消息)按时间排序,同一时间的事件按订阅 → 取消 → 消息的顺序处理。
- 维护活跃消费者:使用动态数组存储活跃消费者索引,后订阅的消费者优先级更高。
- 消息分发:处理消息时,选择优先级最高的活跃消费者(数组末尾的消费者)。
C++ 代码实现
#include <iostream>
#include <vector>
#include <algorithm>
#include <sstream>
#include <string>
#include <iterator>
using namespace std;
// 定义事件结构体
struct Event {
int time; // 事件发生时间
int type; // 0:订阅 1:取消 2:消息
int consumerIdx; // 消费者索引
int message; // 消息内容
Event(int t, int ty, int ci, int m)
: time(t), type(ty), consumerIdx(ci), message(m) {}
};
// 事件排序比较函数
bool compareEvents(const Event& a, const Event& b) {
if (a.time != b.time) {
return a.time < b.time; // 时间升序
} else {
return a.type < b.type; // 同一时间按类型排序
}
}
int main() {
// 读取输入
string msgLine, consumerLine;
getline(cin, msgLine);
getline(cin, consumerLine);
vector<Event> events;
// 解析消息输入
istringstream msgStream(msgLine);
vector<int> msgData;
int num;
while (msgStream >> num) {
msgData.push_back(num);
}
for (size_t i = 0; i < msgData.size(); i += 2) {
events.emplace_back(msgData[i], 2, -1, msgData[i+1]);
}
// 解析消费者输入
istringstream consumerStream(consumerLine);
vector<int> consumerData;
while (consumerStream >> num) {
consumerData.push_back(num);
}
int m = consumerData.size() / 2;
vector<vector<int>> consumers(m); // 存储每个消费者的消息
for (int i = 0; i < m; ++i) {
int subTime = consumerData[2*i];
int unsubTime = consumerData[2*i+1];
events.emplace_back(subTime, 0, i, -1); // 订阅事件
events.emplace_back(unsubTime, 1, i, -1); // 取消事件
}
// 对事件排序
sort(events.begin(), events.end(), compareEvents);
vector<int> activeConsumers; // 当前活跃的消费者索引
// 处理所有事件
for (const auto& event : events) {
switch (event.type) {
case 0: // 订阅
activeConsumers.push_back(event.consumerIdx);
break;
case 1: { // 取消
auto it = find(activeConsumers.begin(),
activeConsumers.end(),
event.consumerIdx);
if (it != activeConsumers.end()) {
activeConsumers.erase(it);
}
break;
}
case 2: // 消息
if (!activeConsumers.empty()) {
int highest = activeConsumers.back();
consumers[highest].push_back(event.message);
}
break;
}
}
// 输出结果
for (const auto& msgs : consumers) {
if (msgs.empty()) {
cout << "-1" << endl;
} else {
for (size_t i = 0; i < msgs.size(); ++i) {
if (i > 0) cout << " ";
cout << msgs[i];
}
cout << endl;
}
}
return 0;
}
代码解析
事件结构体:
struct Event { int time; // 时间 int type; // 类型(0:订阅 1:取消 2:消息) int consumerIdx; // 消费者索引 int message; // 消息内容 };
- 封装事件的时间、类型、消费者索引和消息内容。
输入解析:
istringstream msgStream(msgLine); // 消息输入流 while (msgStream >> num) { ... } // 读取消息数据 for (size_t i = 0; i < msgData.size(); i += 2) { events.emplace_back(msgData[i], 2, -1, msgData[i+1]); // 生成消息事件 }
- 解析消息输入,每两个数字为一组(时间、内容)。
消费者事件处理:
events.emplace_back(subTime, 0, i, -1); // 订阅事件 events.emplace_back(unsubTime, 1, i, -1); // 取消事件
- 每个消费者生成订阅和取消事件。
事件排序:
sort(events.begin(), events.end(), compareEvents);
- 按时间升序排序,同一时间的事件按类型排序(订阅→取消→消息)。
维护活跃消费者:
vector<int> activeConsumers; // 活跃消费者索引数组 case 0: activeConsumers.push_back(event.consumerIdx); // 订阅 case 1: auto it = find(...); // 取消订阅,从数组中删除
- 订阅时将消费者索引加入数组末尾(优先级最高)。
- 取消时线性查找并删除。
消息分发:
int highest = activeConsumers.back(); // 取最后一个元素(优先级最高) consumers[highest].push_back(event.message);
- 消息发送给当前优先级最高的消费者。
示例测试
示例1:
输入:
2 22 1 11 4 44 5 55 3 33
1 7 2 3
输出:
11 33 44 55
22
解析:
- 时刻1:消费者0订阅,处理消息11 → 消费者0接收。
- 时刻2:消费者1订阅,处理消息22 → 消费者1接收。
- 时刻3:消费者1取消,处理消息33 → 消费者0接收。
- 时刻4和5:处理消息44、55 → 消费者0接收。
示例2:
输入:
5 64 11 64 9 97
9 11 4 9
输出:
97
64
解析:
- 时刻5:消息64发送时消费者1活跃 → 消费者1接收。
- 时刻9:消费者0订阅,消费者1取消 → 消息97由消费者0接收。
综合分析
时间复杂度:
- 事件排序:
O(E log E)
,其中E
是事件总数。 - 事件处理:
O(E * M)
,M
是消费者数量(线性查找取消操作)。 - 总体时间复杂度满足题目要求(
E ≤ 20000
,M ≤ 1000
)。
- 事件排序:
空间复杂度:
- 存储事件和消费者消息列表:
O(E + M)
,符合空间限制。
- 存储事件和消费者消息列表:
优势:
- 严格按时间顺序处理事件,逻辑清晰。
- 使用动态数组维护活跃消费者,高效处理优先级。
- 代码结构简洁,易于维护和扩展。
C语言
问题分析
我们需要模拟消息队列的运作,其中发布者在特定时刻发送消息,消费者在特定时刻订阅或取消订阅。消息发送时,优先发送给最后订阅的消费者。同一时刻的事件处理顺序为:订阅 → 取消 → 消息。
解决思路
- 事件统一管理:将所有事件(订阅、取消、消息)统一存储并排序。
- 优先级处理:后订阅的消费者优先级更高,用动态数组维护活跃消费者列表。
- 动态内存管理:使用动态数组存储消费者的消息列表,避免内存浪费。
完整代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_EVENTS 20000
#define MAX_CONSUMERS 1000
#define INIT_CAPACITY 4
// 事件类型枚举
typedef enum { SUBSCRIBE, UNSUBSCRIBE, MESSAGE } EventType;
// 事件结构体
typedef struct {
int time;
EventType type;
int consumer_idx;
int message;
} Event;
// 消费者消息存储结构
typedef struct {
int* messages; // 动态数组存储消息
int size; // 当前消息数量
int capacity; // 数组容量
} Consumer;
/*----------------------------------------------
* 核心函数实现
*--------------------------------------------*/
// 事件排序比较函数
int compare_events(const void* a, const void* b) {
Event* e1 = (Event*)a;
Event* e2 = (Event*)b;
if (e1->time != e2->time) {
return e1->time - e2->time; // 时间升序
}
return (int)e1->type - (int)e2->type; // 订阅→取消→消息
}
// 处理事件并分发消息
void process_events(Event* events, int event_count, Consumer* consumers, int consumer_count) {
int active_consumers[MAX_CONSUMERS]; // 活跃消费者数组
int active_count = 0;
for (int i = 0; i < event_count; ++i) {
Event e = events[i];
switch (e.type) {
case SUBSCRIBE:
// 添加到活跃列表末尾
active_consumers[active_count++] = e.consumer_idx;
break;
case UNSUBSCRIBE:
// 快速移除:用最后一个元素覆盖
for (int j = 0; j < active_count; ++j) {
if (active_consumers[j] == e.consumer_idx) {
active_consumers[j] = active_consumers[active_count - 1];
active_count--;
break;
}
}
break;
case MESSAGE:
if (active_count > 0) {
int target = active_consumers[active_count - 1];
Consumer* c = &consumers[target];
// 动态扩容
if (c->size >= c->capacity) {
int new_cap = c->capacity == 0 ? INIT_CAPACITY : c->capacity * 2;
int* new_messages = realloc(c->messages, new_cap * sizeof(int));
if (new_messages) {
c->messages = new_messages;
c->capacity = new_cap;
}
}
c->messages[c->size++] = e.message;
}
break;
}
}
}
/*----------------------------------------------
* 输入输出处理
*--------------------------------------------*/
// 解析字符串到整数数组
int parse_numbers(char* str, int** result) {
char* token = strtok(str, " ");
int count = 0;
int* arr = NULL;
while (token != NULL) {
arr = realloc(arr, (count + 1) * sizeof(int));
arr[count++] = atoi(token);
token = strtok(NULL, " ");
}
*result = arr;
return count;
}
// 打印消费者消息
void print_consumers(Consumer* consumers, int count) {
for (int i = 0; i < count; ++i) {
if (consumers[i].size == 0) {
printf("-1\n");
} else {
for (int j = 0; j < consumers[i].size; ++j) {
if (j > 0) printf(" ");
printf("%d", consumers[i].messages[j]);
}
printf("\n");
}
}
}
int main() {
// 读取输入
char msg_input[4096], consumer_input[4096];
fgets(msg_input, sizeof(msg_input), stdin);
fgets(consumer_input, sizeof(consumer_input), stdin);
msg_input[strcspn(msg_input, "\n")] = '\0';
consumer_input[strcspn(consumer_input, "\n")] = '\0';
// 解析消息数据
int* msg_data = NULL;
int msg_count = parse_numbers(msg_input, &msg_data);
int message_events = msg_count / 2;
Event* events = malloc(MAX_EVENTS * sizeof(Event));
int event_count = 0;
for (int i = 0; i < message_events; ++i) {
events[event_count++] = (Event){
.time = msg_data[2*i], .type = MESSAGE,
.consumer_idx = -1, .message = msg_data[2*i+1]
};
}
free(msg_data);
// 解析消费者数据
int* consumer_data = NULL;
int consumer_total = parse_numbers(consumer_input, &consumer_data);
int consumer_count = consumer_total / 2;
Consumer consumers[MAX_CONSUMERS] = {0};
for (int i = 0; i < consumer_count; ++i) {
events[event_count++] = (Event){
.time = consumer_data[2*i], .type = SUBSCRIBE,
.consumer_idx = i
};
events[event_count++] = (Event){
.time = consumer_data[2*i+1], .type = UNSUBSCRIBE,
.consumer_idx = i
};
}
free(consumer_data);
// 排序并处理事件
qsort(events, event_count, sizeof(Event), compare_events);
process_events(events, event_count, consumers, consumer_count);
// 输出结果并清理内存
print_consumers(consumers, consumer_count);
for (int i = 0; i < consumer_count; ++i) {
free(consumers[i].messages);
}
free(events);
return 0;
}
代码解析
数据结构设计:
Event
结构体:包含时间、类型、消费者索引和消息内容Consumer
结构体:动态数组存储消息,记录容量和当前大小
事件排序逻辑:
int compare_events(...) {
if (e1->time != e2->time) return 时间差;
return (int)e1->type - (int)e2->type; // SUBSCRIBE < UNSUBSCRIBE < MESSAGE
}
- 动态内存管理:
// 消费者消息数组扩容
if (c->size >= c->capacity) {
int new_cap = c->capacity == 0 ? INIT_CAPACITY : c->capacity * 2;
int* new_messages = realloc(c->messages, new_cap * sizeof(int));
}
- 高效取消订阅处理:
for (int j = 0; j < active_count; ++j) {
if (active_consumers[j] == e.consumer_idx) {
active_consumers[j] = active_consumers[active_count - 1];
active_count--;
break;
}
}
测试用例
输入样例1:
2 22 1 11 4 44 5 55 3 33
1 7 2 3
输出结果:
11 33 44 55
22
输入样例2:
5 64 11 64 9 97
9 11 4 9
输出结果:
97
64
综合分析
时间复杂度:
- 事件排序:O(n log n)
- 事件处理:O(n * m),m为消费者数
- 满足题目数据规模要求
空间复杂度:
- 存储事件和消费者消息:O(n + m)
关键优势:
- 使用动态数组高效管理内存
- 通过排序确保事件处理顺序正确
- 清晰的代码结构便于维护
GO
问题分析
需要模拟消息队列的运作,其中包含发布者和多个消费者。核心要求:
- 事件顺序:订阅事件优先于取消事件,取消事件优先于消息事件。
- 消息分发:消息发送给最后订阅的消费者(优先级最高)。
- 输出:每个消费者收到的消息列表,若无消息则输出
-1
。
解决思路
- 事件统一管理:将订阅、取消、消息事件统一存储,便于排序。
- 排序规则:先按时间升序,同一时间的事件按类型排序(订阅→取消→消息)。
- 维护活跃消费者:使用切片动态管理,后订阅的消费者优先级更高。
- 消息分发:处理消息事件时,选择活跃列表中最后一个消费者。
完整代码实现
package main
import (
"fmt"
"sort"
"strconv"
"strings"
)
// 事件类型枚举
const (
Subscribe = 0
Unsubscribe = 1
Message = 2
)
// Event 结构体
type Event struct {
Time int // 事件时间
Type int // 事件类型
ConsumerIdx int // 消费者索引(用于订阅/取消)
Message int // 消息内容(仅消息事件)
}
func main() {
// 读取输入
var msgInput, consumerInput string
fmt.Scanln(&msgInput)
fmt.Scanln(&consumerInput)
// 解析消息事件
var events []Event
msgParts := strings.Fields(msgInput)
for i := 0; i < len(msgParts); i += 2 {
time, _ := strconv.Atoi(msgParts[i])
message, _ := strconv.Atoi(msgParts[i+1])
events = append(events, Event{
Time: time,
Type: Message,
Message: message,
})
}
// 解析消费者事件
consumerParts := strings.Fields(consumerInput)
m := len(consumerParts) / 2
consumers := make([][]int, m) // 每个消费者的消息列表
for i := 0; i < m; i++ {
subTime, _ := strconv.Atoi(consumerParts[2*i])
unsubTime, _ := strconv.Atoi(consumerParts[2*i+1])
// 添加订阅事件
events = append(events, Event{
Time: subTime,
Type: Subscribe,
ConsumerIdx: i,
})
// 添加取消事件
events = append(events, Event{
Time: unsubTime,
Type: Unsubscribe,
ConsumerIdx: i,
})
}
// 事件排序:时间升序,同时间按类型排序
sort.Slice(events, func(i, j int) bool {
if events[i].Time != events[j].Time {
return events[i].Time < events[j].Time
}
return events[i].Type < events[j].Type
})
// 维护活跃消费者列表
activeConsumers := make([]int, 0)
// 处理事件
for _, event := range events {
switch event.Type {
case Subscribe:
// 将消费者添加到列表末尾(后订阅的优先级更高)
activeConsumers = append(activeConsumers, event.ConsumerIdx)
case Unsubscribe:
// 查找并移除消费者
for idx, cIdx := range activeConsumers {
if cIdx == event.ConsumerIdx {
// 用最后一个元素覆盖并缩短切片
activeConsumers[idx] = activeConsumers[len(activeConsumers)-1]
activeConsumers = activeConsumers[:len(activeConsumers)-1]
break
}
}
case Message:
if len(activeConsumers) > 0 {
// 取优先级最高的消费者(最后订阅的)
target := activeConsumers[len(activeConsumers)-1]
consumers[target] = append(consumers[target], event.Message)
}
}
}
// 输出结果
for _, msgs := range consumers {
if len(msgs) == 0 {
fmt.Println(-1)
} else {
fmt.Println(strings.Trim(fmt.Sprint(msgs), "[]"))
}
}
}
代码解析
- 事件结构体:统一管理事件的时间、类型、消费者索引和消息内容。
- 输入解析:
- 消息事件:每两个数字为一组(时间、内容)。
- 消费者事件:每两个数字为一组(订阅时间、取消时间)。
- 事件排序:按时间升序,同时间事件按类型排序(订阅→取消→消息)。
- 活跃消费者管理:
- 订阅:将消费者索引添加到切片末尾。
- 取消:查找索引并移除,优化为用末尾元素覆盖。
- 消息分发:取切片最后一个消费者(优先级最高)。
- 输出处理:直接打印切片内容,使用
fmt.Sprint
转换为字符串并去除括号。
测试用例
输入1
2 22 1 11 4 44 5 55 3 33
1 7 2 3
输出1
11 33 44 55
22
输入2
5 64 11 64 9 97
9 11 4 9
输出2
97
64
综合分析
- 时间复杂度:
- 事件排序:O(n log n)
- 事件处理:O(n * m)(m 为消费者数量)
- 空间复杂度:存储事件和消费者消息列表,总体为 O(n + m)。
- 优势:
- 利用 Go 切片特性高效管理动态数组。
- 事件处理逻辑清晰,严格遵循时间顺序。
- 代码简洁,易于维护和扩展。
更多内容:
https://www.kdocs.cn/l/cvk0eoGYucWA
本文发表于【纪元A梦】,关注我,获取更多实用教程/资源!