#P2849. 第2题-仓库管理
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 1135
            Accepted: 267
            Difficulty: 5
            
          
          
          
                       所属公司 : 
                              华为
                                
            
                        
              时间 :2025年4月16日-暑期实习(留学生)
                              
                      
          
 
- 
                        算法标签>栈          
 
第2题-仓库管理
题目描述
小华管理一个仓库,支持四种操作:
in s:将编号为字符串s(仅包含小写字母和数字,且各不相同)的产品入库。out:将最晚入库的产品出库,并输出其编号;若仓库空,输出EMPTY。count:输出当前仓库中产品的数量。check:输出仓库中编号按字典序最小的产品;若仓库空,输出EMPTY。
要求在 q≤2×105 次操作内高效完成上述四种操作。
解题思路
数据结构设计
- 栈(stack):维护入库顺序,用于 
out操作快速取出“最晚入库”产品。 - 最小堆(min‑heap) + 哈希集合(hash set)(Python 方案)
check操作需要快速获取字典序最小者;使用堆顶即可。- 由于 
out从栈中删除最新元素,需要在堆中做“懒删除”:出库时只从集合中移除,不立即从堆里删除;check时弹出堆顶直到它在集合里才输出。 
 - 平衡二叉搜索树(Java/C++ 方案)
- 使用 
TreeSet(Java)或std::multiset(C++)维护所有在库编号,支持插入、删除、取最小均为 O(logn)。 
 - 使用 
 
操作实现
in s- 栈 
push(s);集合/树插入s;堆push(s)(Python)。 
- 栈 
 out- 如果栈空,输出 
EMPTY;否则栈顶pop()得到编号s,并从集合/树中删除,输出s。 
- 如果栈空,输出 
 count- 输出集合/树的大小。
 
check- Python:弹出堆顶直到该编号在集合中或堆空;若堆空输出 
EMPTY,否则输出堆顶但不弹出。 - Java/C++:若树为空输出 
EMPTY,否则输出first()/*begin()。 
- Python:弹出堆顶直到该编号在集合中或堆空;若堆空输出 
 
复杂度分析
- Python(堆 + 哈希集合):
in:O(logn)(堆)out:O(1)(栈) + O(1)(集合)check:均摊 O(logn)(堆)
 - Java/C++(栈 + 平衡树):
- 所有操作均为 O(logn)(插入/删除/查找/取最小)或 O(1)(栈)
 
 - 总体时间复杂度 O(qlogq),空间复杂度 O(q)。
 
代码实现
Python
import sys
import heapq
def main():
    input = sys.stdin.readline
    q = int(input())
    stack = []
    min_heap = []
    in_set = set()
    
    for _ in range(q):
        op = input().split()
        if op[0] == 'in':
            s = op[1]
            stack.append(s)            # 记录入库顺序
            heapq.heappush(min_heap, s)  # 用于 check
            in_set.add(s)              # 在库集合
        elif op[0] == 'out':
            # 出库最晚入库的产品
            if not stack:
                print('EMPTY')
            else:
                s = stack.pop()
                in_set.remove(s)
                print(s)
        elif op[0] == 'count':
            print(len(in_set))
        elif op[0] == 'check':
            # 查询字典序最小
            while min_heap and min_heap[0] not in in_set:
                heapq.heappop(min_heap)
            if not min_heap:
                print('EMPTY')
            else:
                print(min_heap[0])
if __name__ == '__main__':
    main()
Java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.*;
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int q = Integer.parseInt(br.readLine());
        Deque<String> stack = new ArrayDeque<>();
        TreeSet<String> tree = new TreeSet<>();
        for (int i = 0; i < q; i++) {
            String[] parts = br.readLine().split(" ");
            String cmd = parts[0];
            if (cmd.equals("in")) {
                String s = parts[1];
                stack.push(s);     // 入栈记录顺序
                tree.add(s);       // 树集维护字典序
            } else if (cmd.equals("out")) {
                // 出库最晚入库的产品
                if (stack.isEmpty()) {
                    System.out.println("EMPTY");
                } else {
                    String s = stack.pop();
                    tree.remove(s);
                    System.out.println(s);
                }
            } else if (cmd.equals("count")) {
                System.out.println(tree.size());
            } else if (cmd.equals("check")) {
                if (tree.isEmpty()) {
                    System.out.println("EMPTY");
                } else {
                    System.out.println(tree.first());
                }
            }
        }
    }
}
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int q;
    cin >> q;
    vector<string> stack_;
    multiset<string> ms;
    while (q--) {
        string cmd;
        cin >> cmd;
        if (cmd == "in") {
            string s;
            cin >> s;
            stack_.push_back(s);  // 记录入库顺序
            ms.insert(s);         // multiset 维护字典序
        }
        else if (cmd == "out") {
            // 出库最晚入库的产品
            if (stack_.empty()) {
                cout << "EMPTY\n";
            } else {
                string s = stack_.back();
                stack_.pop_back();
                ms.erase(ms.find(s));
                cout << s << "\n";
            }
        }
        else if (cmd == "count") {
            cout << ms.size() << "\n";
        }
        else if (cmd == "check") {
            if (ms.empty()) {
                cout << "EMPTY\n";
            } else {
                cout << *ms.begin() << "\n";
            }
        }
    }
    return 0;
}
        题目内容
小华是一个仓库管理员,他的日常就是管理仓库产品的入库出库。
有四种操作:
in s,给一个只包含小写字符和数字的字符串 s ,表示一个新的产品入库, 产品编号为 s 。
out ,表示一个出库请求,小华会将最晚入库的产品出库。
count ,表示清点仓库里的产品数量。
check ,表示查询仓库里编号字典序最小的产品。(字典序的顺序是先按照 第一个字符以升序排列;如果第一个字符一样,那就比较第二个、第三个 乃至后面的字母。如果比到最后两个串不一样长(比如,123 和 12345 ),那么把短者排在前。)
输入描述
第一行一个整数 q(1<=q<=200000) 表示操作的次数。
接下来 q 行每行一个操作,为四种操作之一。
保证所有产品的编号都是仅包含数字和小写字母的字符串,每个字符串长 度不超过 20 。
保证所有产品的编号互不相同。
输出描述
对于每个 out 操作,输出一行,包含一个字符串,表示被出库的产品编号。
如果没有能出库的产品输出 EMPTY 。
对于每个 count 操作,输出一行,包含一个整数,表示当前仓库里的产品 数量。
样例1
输入
13
check
in c1
in c2
count
check
in b3
in b4
count
check
out
check
out
check
输出
EMPTY
2
c1
4
b3
b4
b3
b3
c1
说明
首先 check 操作,此时仓库里没有产品输出 EMPTY。
后面 in c1 in c2 两个操作,仓库中有 c1 ,c2 两个产品,因此 count 操 作,输出为 2 。
check 输出仓库里编号字典序最小的产品编号 c1。
然后 in b3 in b4 ,仓库中有 c1,c2,b3,b4 四个产品,count 操作输出 4 ,check 输出仓库里编号字典序最小的产品编号 b3 ,out 将最晚入 库的产品出库,输出 b4 ,check 输出仓库里编号字典序最小的产品编号 b3 。out 将最晚入库的产品出库,输出 b3 ,此时仓库只有 c1,c2 两个 产品,check 输出仓库里编号最小的产品编号 c1 。