题目链接
题目描述
模拟一个简化的深空分布式文件系统的命令行功能。该文件系统是一个树状结构,初始时存在一个 /usr 目录,且当前工作目录就在 usr。需要处理一系列命令并对 ls 命令作出响应。
-
支持的指令:
mkdir <dirname>: 在当前目录下创建新目录。如果已存在则忽略。cd ..: 切换到父目录。cd <dirname>: 切换到当前目录下的指定子目录。ls: 按字典序升序,列出当前目录下的所有子目录。
-
输出要求:
- 对于每个
ls指令,输出结果占一行。 - 目录名之间用单个空格分隔。
- 如果当前目录为空,
ls指令输出一个单独的空格。
- 对于每个
解题思路
本题的核心是模拟一个树形文件系统的操作。我们可以通过构建一个显式的树结构来解决问题,其中每个节点代表一个目录。
-
数据结构设计
- 我们定义一个
Node类(或结构体)来表示一个目录。 - 每个
Node包含三个关键部分:name(string): 目录的名称。parent(Node*): 一个指向父目录节点的指针。根目录的父节点可以设为nullptr或自身。children(map<string, Node*>): 一个存储所有子目录的映射。选择map(或 Java 中的TreeMap) 是此题的关键,因为它能同时满足两个需求:- 快速查找:通过目录名(键)快速定位子目录,时间复杂度为对数级别,适用于
cd <dirname>和mkdir的存在性检查。 - 自动排序:
map会自动根据键(目录名)按字典序排序,这使得ls命令的实现变得非常简单,只需按顺序遍历map即可。
- 快速查找:通过目录名(键)快速定位子目录,时间复杂度为对数级别,适用于
- 我们定义一个
-
系统初始化
- 首先,创建一个根节点
root,代表根目录/。将其parent指向自身或nullptr。 - 在
root下创建一个名为usr的子节点。 - 维护一个全局的
currentNode指针,用于表示“当前工作目录”,并将其初始化为usr节点。
- 首先,创建一个根节点
-
命令模拟
- 循环读取每条命令,并根据命令类型进行分派。
mkdir <dirname>:- 检查
currentNode->children中是否已存在键为<dirname>的项。 - 如果不存在,则创建一个新的
Node,设置其parent为currentNode,然后将其插入到currentNode->children中。
- 检查
cd ..:- 检查
currentNode->parent是否为空或指向自身。 - 如果不为空,则将
currentNode更新为其父节点currentNode = currentNode->parent。
- 检查
cd <dirname>:- 在
currentNode->children中查找键为<dirname>的项。 - 如果找到了,则将
currentNode更新为该子节点currentNode = currentNode->children[<dirname>]。
- 在
ls:- 检查
currentNode->children是否为空。 - 如果为空,输出一个空格。
- 如果不为空,则遍历
map的所有键,将它们用空格连接并输出。
- 检查
通过这种方式,我们可以清晰且高效地模拟文件系统的所有操作。
代码
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <sstream>
using namespace std;
struct Node {
string name;
Node* parent;
map<string, Node*> children;
Node(string n, Node* p) : name(n), parent(p) {}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin >> n;
Node* root = new Node("/", nullptr);
root->parent = root; // 根节点的父节点指向自己
Node* usr = new Node("usr", root);
root->children["usr"] = usr;
Node* currentNode = usr;
string command;
for (int i = 0; i < n; ++i) {
cin >> command;
if (command == "mkdir") {
string dirname;
cin >> dirname;
if (currentNode->children.find(dirname) == currentNode->children.end()) {
currentNode->children[dirname] = new Node(dirname, currentNode);
}
} else if (command == "cd") {
string target;
cin >> target;
if (target == "..") {
currentNode = currentNode->parent;
} else {
if (currentNode->children.count(target)) {
currentNode = currentNode->children[target];
}
}
} else if (command == "ls") {
if (currentNode->children.empty()) {
cout << " " << "\n";
} else {
bool first = true;
for (auto const& [key, val] : currentNode->children) {
if (!first) {
cout << " ";
}
cout << key;
first = false;
}
cout << "\n";
}
}
}
// Note: In a real application, memory for Nodes should be deallocated.
// For competitive programming, this is often omitted for speed.
return 0;
}
import java.util.Scanner;
import java.util.Map;
import java.util.TreeMap;
import java.util.StringJoiner;
public class Main {
static class Node {
String name;
Node parent;
Map<String, Node> children = new TreeMap<>();
Node(String name, Node parent) {
this.name = name;
this.parent = parent;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Node root = new Node("/", null);
root.parent = root; // 根节点的父节点指向自己
Node usr = new Node("usr", root);
root.children.put("usr", usr);
Node currentNode = usr;
for (int i = 0; i < n; i++) {
String command = sc.next();
if (command.equals("mkdir")) {
String dirname = sc.next();
if (!currentNode.children.containsKey(dirname)) {
currentNode.children.put(dirname, new Node(dirname, currentNode));
}
} else if (command.equals("cd")) {
String target = sc.next();
if (target.equals("..")) {
currentNode = currentNode.parent;
} else {
if (currentNode.children.containsKey(target)) {
currentNode = currentNode.children.get(target);
}
}
} else if (command.equals("ls")) {
if (currentNode.children.isEmpty()) {
System.out.println(" ");
} else {
StringJoiner sj = new StringJoiner(" ");
for (String key : currentNode.children.keySet()) {
sj.add(key);
}
System.out.println(sj.toString());
}
}
}
}
}
import sys
class Node:
def __init__(self, name, parent=None):
self.name = name
self.parent = parent
self.children = {}
def main():
n = int(input())
root = Node("/")
root.parent = root # 根节点的父节点指向自己
usr = Node("usr", root)
root.children["usr"] = usr
current_node = usr
for _ in range(n):
line = input().split()
command = line[0]
if command == "mkdir":
dirname = line[1]
if dirname not in current_node.children:
current_node.children[dirname] = Node(dirname, current_node)
elif command == "cd":
target = line[1]
if target == "..":
current_node = current_node.parent
else:
if target in current_node.children:
current_node = current_node.children[target]
elif command == "ls":
if not current_node.children:
print(" ")
else:
# Python的dict需要手动排序key
sorted_dirs = sorted(current_node.children.keys())
print(" ".join(sorted_dirs))
if __name__ == "__main__":
main()
算法及复杂度
- 算法: 树、模拟
- 时间复杂度:
。
是总指令数。对于每条指令:
mkdir和cd <dirname>的操作主要是对map进行查找和插入,时间复杂度为,其中
是当前目录下子目录的最大数量。
cd ..的时间复杂度为。
ls的操作需要遍历当前目录下的所有子目录并排序(Python)。对于 C++map和 JavaTreeMap遍历是,Python 排序是
。输出字符串的长度也会影响,但主要瓶颈是排序和查找。因此,总的来说,单次操作最坏情况是
。
- 空间复杂度:
。
是创建的目录总数,
是目录名的平均长度。空间主要用于存储树的节点,每个节点包含名称、父指针和子目录映射。

京公网安备 11010502036488号