ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

华为OD机试:虚拟文件系统的树形结构实现

华为OD机试:虚拟文件系统的树形结构实现 1. 题目背景与需求分析这道华为OD机试题要求我们实现一个虚拟文件系统支持两种核心操作添加文件(addfile)和展示目录内容(ls)。从实际应用场景来看这类题目考察的是对树形数据结构的理解和操作能力这在软件开发中非常常见比如操作系统中的文件系统管理前端路由的路径匹配配置中心的层级配置管理题目给出的具体需求是addfile /path/to/file将文件添加到指定路径ls /path列出指定路径下的所有内容其中文件夹需要以*标识2. 数据结构设计与选型2.1 树形结构的选择要实现这个虚拟文件系统最直观的方式是使用树形结构。每个节点可以表示一个目录或文件目录节点包含子节点可以是目录或文件文件节点末端节点不包含子节点在Java中我们可以用嵌套的MapString, Object来表示MapString, Object root new HashMap(); // 目录节点value是另一个Map // 文件节点value是null在Go中可以定义更明确的结构体type Node struct { children map[string]*Node // 子目录 files map[string]bool // 子文件 }2.2 路径处理的注意事项路径处理有几个关键点需要注意路径可能以/开头或结尾需要先去除空路径表示根目录路径分隔符是/示例处理代码JavaString path /src/main/java/; String trimmed path.replaceAll(^/|/$, ); // 去除首尾斜杠 String[] parts trimmed.split(/); // 拆分路径3. 核心算法实现3.1 添加文件(addfile)实现添加文件的逻辑可以分为以下步骤解析路径拆分成各级目录名和文件名从根节点开始逐级查找或创建目录节点在最后一级目录下创建文件节点Java实现关键代码MapString, Object node root; for (int i 0; i parts.length - 1; i) { if (!node.containsKey(parts[i])) { node.put(parts[i], new HashMapString, Object()); } node (MapString, Object) node.get(parts[i]); } node.put(parts[parts.length - 1], null); // 最后一段是文件名3.2 列出目录(ls)实现列出目录内容的逻辑解析路径找到目标目录节点收集该目录下的所有子项对子项进行排序并格式化输出目录加*Java实现示例ListString items new ArrayList(); for (Map.EntryString, Object entry : node.entrySet()) { if (entry.getValue() instanceof Map) { items.add(entry.getKey() *); // 目录 } else { items.add(entry.getKey()); // 文件 } } Collections.sort(items); System.out.println(String.join( , items));4. 完整代码实现4.1 Java版本import java.util.*; public class VirtualFileSystem { public static void main(String[] args) { MapString, Object root new HashMap(); Scanner sc new Scanner(System.in); ListString lines new ArrayList(); while (sc.hasNextLine()) { String line sc.nextLine().trim(); if (!line.isEmpty()) lines.add(line); } String lsPath null; for (String line : lines) { if (line.startsWith(addfile )) { String path line.substring(8); String[] parts path.replaceAll(^/|/$, ).split(/); MapString, Object node root; for (int i 0; i parts.length - 1; i) { node.putIfAbsent(parts[i], new HashMapString, Object()); node (MapString, Object) node.get(parts[i]); } node.put(parts[parts.length - 1], null); } else if (line.startsWith(ls )) { lsPath line.substring(3); } } if (lsPath ! null) { String[] parts lsPath.replaceAll(^/|/$, ).split(/); MapString, Object node root; for (String p : parts) { if (!node.containsKey(p) || !(node.get(p) instanceof Map)) { System.out.println(); return; } node (MapString, Object) node.get(p); } ListString items new ArrayList(); for (Map.EntryString, Object entry : node.entrySet()) { items.add(entry.getKey() (entry.getValue() instanceof Map ? * : )); } Collections.sort(items); System.out.println(String.join( , items)); } } }4.2 Go版本package main import ( bufio fmt os sort strings ) type Node struct { children map[string]*Node files map[string]bool } func newNode() *Node { return Node{ children: make(map[string]*Node), files: make(map[string]bool), } } func splitPath(path string) []string { trimmed : strings.Trim(path, /) if trimmed { return []string{} } return strings.Split(trimmed, /) } func main() { root : newNode() scanner : bufio.NewScanner(os.Stdin) var lines []string for scanner.Scan() { line : scanner.Text() if line ! { lines append(lines, line) } } var lsPath string for _, line : range lines { if strings.HasPrefix(line, addfile ) { path : line[8:] parts : splitPath(path) node : root for i : 0; i len(parts)-1; i { if _, exists : node.children[parts[i]]; !exists { node.children[parts[i]] newNode() } node node.children[parts[i]] } node.files[parts[len(parts)-1]] true } else if strings.HasPrefix(line, ls ) { lsPath line[3:] } } if lsPath ! { parts : splitPath(lsPath) node : root valid : true for _, p : range parts { if child, exists : node.children[p]; exists { node child } else { valid false break } } if !valid { fmt.Println() return } var items []string for name : range node.children { items append(items, name*) } for name : range node.files { items append(items, name) } sort.Strings(items) fmt.Println(strings.Join(items, )) } }5. 测试用例与验证5.1 测试用例设计好的测试用例应该覆盖以下场景添加文件到多级目录列出空目录列出包含文件和子目录的目录处理根目录的特殊情况处理不存在的路径示例测试输入addfile /src/main/java/Test.java addfile /src/main/resources/config.yml addfile /src/test/java/Test.java ls /src ls /src/main ls /nonexistent预期输出main* test* java* resources* config.yml Test.java5.2 边界情况处理需要特别注意的边界情况根目录路径处理/或重复添加同一文件路径中包含多个连续的/文件名或目录名包含特殊字符处理建议// 处理连续的斜杠和首尾斜杠 path path.replaceAll(/, /).replaceAll(^/|/$, );6. 性能优化与扩展6.1 性能考量对于大规模文件系统可以考虑以下优化使用更高效的数据结构如Trie树实现惰性加载只在访问时创建节点添加缓存机制存储常用路径的查找结果6.2 功能扩展实际文件系统通常还支持删除文件/目录移动/重命名文件权限管理文件内容存储扩展实现示例Java// 删除文件 public void deleteFile(MapString, Object root, String path) { String[] parts path.replaceAll(^/|/$, ).split(/); MapString, Object node root; for (int i 0; i parts.length - 1; i) { node (MapString, Object) node.get(parts[i]); if (node null) return; } node.remove(parts[parts.length - 1]); }7. 面试技巧与注意事项7.1 解题思路遇到这类题目时建议先明确需求和边界条件设计合适的数据结构分步骤实现各个功能编写测试用例验证7.2 常见错误需要注意的常见错误路径处理不完整首尾斜杠、连续斜杠类型判断错误文件vs目录空指针异常访问不存在的路径排序规则不一致7.3 代码风格建议良好的代码风格包括合理的变量命名适当的代码注释错误处理机制模块化设计例如可以将文件系统操作封装成类public class VirtualFileSystem { private MapString, Object root; public VirtualFileSystem() { root new HashMap(); } public void addFile(String path) { ... } public String listDirectory(String path) { ... } }8. 总结与进阶学习实现虚拟文件系统是理解树形数据结构的绝佳练习。掌握这个基础后可以进一步学习真实的文件系统实现如FAT、NTFS、ext4分布式文件系统如HDFS内存文件系统如Linux的tmpfs版本控制系统如Git的存储模型建议的实践方向添加文件内容存储功能实现文件查找功能支持通配符添加文件元数据大小、创建时间等实现持久化存储保存到磁盘
返回列表