简易内存文件系统模拟
请模拟并实现一套简易的、基于内存的文件系统,该系统支持目录和文件的创建、删除、切换以及内容查询。
核心概念
-
树形结构: 文件系统是一个以根目录
/为起点的树形结构。 -
路径 (Path):
- 绝对路径: 以
/开头,表示从根目录开始的完整路径,例如/dir_a/dir_b。 - 相对路径: 不以
/开头,表示相对于当前目录的路径,例如dir_x/dir_y。
- 绝对路径: 以
-
名称唯一性: 在同一个目录下,所有文件和子目录的名称必须是唯一的。
-
初始状态: 系统初始化后,只有一个根目录
/,光标(当前工作目录)也位于根目录。
功能要求
你需要实现一个类,支持以下操作:
FileSystem()
- 功能: 系统初始化。
- 初始状态: 当前目录在根目录
/,且根目录下为空。
makeDir(string dirName)
- 功能: 在当前目录下创建一个新的子目录。
- 成功条件:
dirName与当前目录下的任何已有文件或目录的名称都不重复。 - 返回值: 创建成功返回
true,否则返回false。
createFile(string fileName)
- 功能: 在当前目录下创建一个新的文件。
- 成功条件:
fileName与当前目录下的任何已有文件或目录的名称都不重复。 - 返回值: 创建成功返回
true,否则返回false。
changeDir(string pathName)
-
功能: 切换当前工作目录到
pathName指定的位置。 -
规则:
- 如果
pathName为""(空字符串),当前目录保持不变,返回true。 - 如果
pathName指定的目录存在,则切换成功,返回true。 - 如果
pathName指定的目录不存在,则切换失败,当前目录保持不变,返回false。
- 如果
-
注意:
pathName可以是绝对路径或相对路径,且末尾可能带有一个/。
remove(string name)
- 功能: 删除当前目录下名称为
name的文件或目录。 - 成功条件: 名称为
name的文件或目录在当前目录下存在。 - 注意 (递归删除): 如果删除的是一个目录,那么该目录下的所有子目录和文件也需要一并被递归删除。
- 返回值: 删除成功返回
true,否则返回false。
listDir()
-
功能: 返回当前目录下的所有目录和文件的名称列表。
-
返回要求:
- 不包含子目录下的内容。
- 排序规则: 目录在前,文件在后;在各自的类别中,按字典序升序排列。
输入格式
-
inputStr(用于构造函数或初始化的字符串,在样例中未直接体现,以dirTreeLines形式给出):- 题目中的样例输入实际上是 LeetCode 竞赛的一种输入格式,每行表示一个函数调用。
-
dirName,fileName,name:- 仅包含小写字母。
- 长度范围:
[1, 100]。
-
pathName:- 仅包含小写字母和
/。 0 <= pathName.length < 1000。- 路径中不包含
.或..。 - 非空时为合法目录名,末尾可能带
/。
- 仅包含小写字母和
输出格式
- 根据每个函数的原型要求返回相应的值。最终的整体输出由评测框架完成。
样例
输入样例 1
["FileSystem", "makeDir", "makeDir", "makeDir", "listDir", "changeDir", "createFile", "makeDir", "createFile", "listDir", "changeDir"]
[[], ["dirc"], ["dirb"], ["dirc"], [], ["dirc/"], ["fileb"], ["dirb"], ["dirb"], [], ["/dirb/dirc"]]
输出样例 1
[null, true, true, false, ["dirb", "dirc"], true, true, true, false, ["dirb", "fileb"], false]
样例 1 解释
| # | 调用 | 内部状态/解释 | 返回值 |
|---|---|---|---|
| 1 | FileSystem() | 初始化成功,当前在根目录 /。 | null |
| 2 | makeDir("dirc") | 在 / 下创建目录 dirc,成功。 | true |
| 3 | makeDir("dirb") | 在 / 下创建目录 dirb,成功。 | true |
| 4 | makeDir("dirc") | 目录 dirc 在 / 下已存在,创建失败。 | false |
| 5 | listDir() | 列出 / 下内容,目录按字典序为 dirb, dirc。 | ["dirb", "dirc"] |
| 6 | changeDir("dirc/") | 切换到 dirc 目录(末尾的 / 被忽略),成功。 | true |
| 7 | createFile("fileb") | 在 /dirc 下创建文件 fileb,成功。 | true |
| 8 | makeDir("dirb") | 在 /dirc 下创建目录 dirb,成功。 | true |
| 9 | createFile("dirb") | 目录 dirb 在 /dirc 下已存在,文件名冲突,创建失败。 | false |
| 10 | listDir() | 列出 /dirc 下内容,目录 dirb 在前,文件 fileb 在后。 | ["dirb", "fileb"] |
| 11 | changeDir("/dirb/dirc") | 尝试切换到绝对路径 /dirb/dirc,但该目录不存在,失败。 | false |
import java.util.*;
public class FileSystem {
// --- 内部数据结构定义 ---
private abstract static class Node {
String name;
public Node(String name) { this.name = name; }
}
private static class FileNode extends Node {
public FileNode(String name) { super(name); }
}
private static class DirectoryNode extends Node {
// TreeMap 自动按 key(文件名/目录名) 的字典序排序
Map<String, Node> children = new TreeMap<>();
public DirectoryNode(String name) { super(name); }
}
// --- FileSystem 类的成员变量 ---
private final DirectoryNode root;
private DirectoryNode currentNode;
// --- 公共方法实现 ---
public FileSystem() {
this.root = new DirectoryNode("/");
this.currentNode = this.root;
}
public boolean makeDir(String dirName) {
if (currentNode.children.containsKey(dirName)) {
return false;
}
DirectoryNode newDir = new DirectoryNode(dirName);
currentNode.children.put(dirName, newDir);
return true;
}
public boolean createFile(String fileName) {
if (currentNode.children.containsKey(fileName)) {
return false;
}
FileNode newFile = new FileNode(fileName);
currentNode.children.put(fileName, newFile);
return true;
}
public boolean changeDir(String pathName) {
if (pathName.isEmpty()) {
return true;
}
DirectoryNode targetDir = resolvePathToDirectory(pathName);
if (targetDir != null) {
this.currentNode = targetDir;
return true;
}
return false;
}
public boolean remove(String name) {
if (currentNode.children.containsKey(name)) {
currentNode.children.remove(name);
return true;
}
return false;
}
public List<String> listDir() {
List<String> dirNames = new ArrayList<>();
List<String> fileNames = new ArrayList<>();
// TreeMap保证了遍历顺序是按key的字典序
for (Map.Entry<String, Node> entry : currentNode.children.entrySet()) {
if (entry.getValue() instanceof DirectoryNode) {
dirNames.add(entry.getKey());
} else {
fileNames.add(entry.getKey());
}
}
List<String> result = new ArrayList<>(dirNames);
result.addAll(fileNames);
return result;
}
// --- 私有辅助方法 ---
private DirectoryNode resolvePathToDirectory(String path) {
DirectoryNode startNode = path.startsWith("/") ? root : currentNode;
String[] parts = path.split("/");
DirectoryNode tempNode = startNode;
for (String part : parts) {
if (part.isEmpty()) {
continue; // 处理 "a//b" 或 "/a" "/a/" 这样的情况
}
Node nextNode = tempNode.children.get(part);
if (nextNode == null || nextNode instanceof FileNode) {
return null; // 路径不存在或者是文件
}
tempNode = (DirectoryNode) nextNode;
}
return tempNode;
}
}