題目描述:設計一個記憶體內的檔案系統,支援以下四個操作:ls 列出目錄或檔案;mkdir 建立目錄(包含所有中間目錄);addContentToFile 寫入(追加)檔案內容;readContentFromFile 讀取檔案全部內容。
解題思路:使用 Trie(前綴樹)建模檔案系統。每個 Trie 節點包含:
children:map<string, TrieNode*> 儲存子目錄/檔案節點(使用 map 保證字典序)content:string 儲存檔案內容(非空代表這是一個檔案節點)所有操作都從根節點出發,依據路徑字串以 / 分割,逐步向下走訪或建立節點:
ls:走訪到目標節點;若是檔案則返回 [filename],若是目錄則返回 children 的所有鍵(map 已排序)。mkdir:依路徑逐段建立不存在的節點。addContentToFile:走訪到檔案節點(途中建立不存在的目錄),將 content 追加至節點的 content 字串。readContentFromFile:走訪到檔案節點,返回其 content 字串。時間複雜度:每次操作均為 O(L + K log K),其中 L 為路徑中的總字元數(用於走訪 / 建立節點),K 為目標目錄下的子項目數(ls 操作需遍歷 map)。由於 map 已排序,ls 輸出無需額外排序。
空間複雜度:O(N × L) — N 為所有唯一路徑段的數量,L 為各段平均長度;每個節點的 children map 在最壞情況下存儲所有子節點。檔案內容額外佔 O(C) 其中 C 為所有寫入內容的總長度。
1. Define TrieNode with: map<string,TrieNode*> children, string content. 2. Initialize root = new TrieNode(). 3. Helper traverse(path, create=false): a. Split path by '/', skipping empty tokens. b. For each token: if create and child missing, create it. Move cur to child. c. Return cur node. 4. ls(path): a. node = traverse(path). b. If node.content is non-empty: return [last segment of path]. c. Else: return list of node.children keys (already sorted). 5. mkdir(path): traverse(path, create=true). 6. addContentToFile(filePath, content): a. node = traverse(filePath, create=true). b. node.content += content. 7. readContentFromFile(filePath): return traverse(filePath).content.
方法一:Trie + map(本題主解)O(L + K log K)
每個節點用 map<string, TrieNode*> 儲存子節點,map 按字典序排序,ls 輸出天然有序。路徑走訪 O(L),ls O(K)(已排序無需額外 sort)。
方法二:Trie + unordered_map + 按需排序 O(L + K log K)
子節點改用 unordered_map 以加速插入/查找(O(1) 均攤),但 ls 時需對 keys 進行 sort,最終複雜度相同。在子節點數量 K 極大時略優於 map 的插入,但 ls 時排序開銷相同。
方法三:HashMap 模擬完整路徑 O(L)
不使用 Trie,直接用 unordered_map<string, set<string>> 存目錄內容,unordered_map<string, string> 存檔案內容。ls 操作回傳 set 元素(已排序),mkdir/addContent 直接更新 map。實作更平鋪,但維護目錄與檔案兩個獨立的 map 需仔細同步。
rm(刪除檔案或目錄),應如何設計遞迴刪除並釋放記憶體?需要注意哪些邊界情況(如刪除非空目錄)?mv(移動/重命名),在 Trie 結構下如何高效實現而不需要重建整個子樹?