题目链接
题目描述
你需要维护一个字符串序列(多重集合),支持以下三种操作:
- 插入:
1 s
,将字符串s
加入序列。 - 查询:
2
,输出序列中字典序最小的字符串。 - 删除:
3
,删除序列中字典序最小的字符串(若有多个,只删除一个)。
这是一个主函数模式的题目,你需要使用官方提供的模板来填充函数。
输入格式:
- 第一行是一个整数
,表示操作的总数。
- 接下来
行,每行描述一个操作。
输出格式:
- 对于每个查询操作(类型 2),输出一行,为当前序列中字典序最小的字符串。
示例: 输入:
5
1 abc
1 cda
2
3
2
输出:
abc
cda
解题思路
这个问题要求我们动态地维护一个集合,并能高效地进行插入、查询字典序最小值和删除字典序最小值。这和我们之前处理整数优先队列的逻辑完全相同,只是操作的对象从整数变为了字符串。
最小优先队列 (Min-Priority Queue) 依然是解决此问题的最佳数据结构。幸运的是,所有主流语言的标准库都为字符串提供了默认的字典序比较,这意味着我们可以直接将字符串放入优先队列,而不需要像上一题那样编写复杂的自定义比较器。
各语言实现
- C++:
std::priority_queue
默认是最大堆。为了找到字典序最小的字符串,我们需要一个最小堆。可以通过向模板提供std::greater<string>
比较器来实现。题目模板已经为我们设置好了priority_queue<string, vector<string>, greater<string>> s;
。 - Java:
java.util.PriorityQueue
对字符串默认就是按字典序升序排列的最小堆,因为String
类实现了Comparable
接口。题目模板提供的new PriorityQueue<>()
可以直接使用。 - Python:
heapq
模块在列表上实现一个最小堆。字符串在 Python 中默认按字典序比较,所以直接将字符串推入列表即可。
算法步骤:
- 根据题目模板,在
insertValue
,deleteValue
,getTop
函数中封装对全局优先队列的相应操作。insertValue(x)
: 将字符串x
推入堆。deleteValue()
: 弹出堆顶元素。getTop()
: 返回堆顶元素。
- 在执行
delete
和get
操作前,需要判断队列是否为空。 - 主函数逻辑由模板提供,它会调用我们填充的函数来完成任务。
代码
#include<bits/stdc++.h>
using namespace std;
// 使用 greater<string> 将默认的最大堆转为最小堆
priority_queue<string, vector<string>, greater<string>> s;
void insertValue(string x){
s.push(x);
}
void deleteValue(){
if (!s.empty()) {
s.pop();
}
}
string getTop(){
if (!s.empty()) {
return s.top();
}
return ""; // Or handle as per problem constraints on empty query
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int q,op;
string x;
cin>>q;
while(q--){
cin>>op;
if(op==1){
cin>>x;
insertValue(x);
}
if(op==2){
cout<<getTop()<<endl;
}
if(op==3){
deleteValue();
}
}
return 0;
}
import java.util.*;
public class Main {
// String 实现了 Comparable,PriorityQueue 默认就是按字典序的最小堆
static PriorityQueue<String> s = new PriorityQueue<>();
public static void insertValue(String x) {
s.add(x);
}
public static void deleteValue() {
if (!s.isEmpty()) {
s.poll();
}
}
public static String getTop() {
if (!s.isEmpty()) {
return s.peek();
}
return ""; // Or handle as per problem constraints
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int q = scanner.nextInt();
while (q-- > 0) {
int op = scanner.nextInt();
if (op == 1) {
String x = scanner.next();
insertValue(x);
}
if (op == 2) {
System.out.println(getTop());
}
if (op == 3) {
deleteValue();
}
}
scanner.close();
}
}
import heapq
# heapq 是最小堆,字符串默认按字典序比较
s = []
def insertValue(x):
heapq.heappush(s, x)
def deleteValue():
if s:
heapq.heappop(s)
def getTop():
if s:
return s[0]
return "" # Or handle as per problem constraints
if __name__ == "__main__":
q = int(input())
for _ in range(q):
line = input().split()
op = int(line[0])
if op == 1:
x = line[1]
insertValue(x)
elif op == 2:
print(getTop())
elif op == 3:
deleteValue()
算法及复杂度
- 数据结构: 优先队列(最小堆)
- 时间复杂度: 对于
次操作,插入和删除的代价是
,查询是
,其中
是堆中当前的元素数量,
是字符串的平均长度(因为字符串比较的代价与长度有关)。总时间复杂度大致为
。
- 空间复杂度:
,即所有插入的字符串的总长度。