Remove Sub-Folders from the Filesystem - Complete Solution Guide
Remove Sub-Folders from the Filesystem is LeetCode problem 1233, a Medium level challenge. This complete guide provides step-by-step explanations, multiple solution approaches, and optimized code in python3, java, cpp, c.
Problem Statement
Given a list of folders folder , return the folders after removing all sub-folders in those folders . You may return the answer in any order . If a folder[i] is located within another folder[j] , it is called a sub-folder of it. A sub-folder of folder[j] must start with folder[j] , followed by a "/" . For example, "/a/b" is a sub-folder of "/a" , but "/b" is not a sub-folder of "/a/b/c" . The format of a path is one or more concatenated strings of the form: '/' followed by one or more lowercase
Detailed Explanation
The problem asks us to remove sub-folders from a given list of folder paths. A sub-folder is defined as a folder whose path starts with another folder's path, followed by a '/'. For example, if we have '/a' and '/a/b', then '/a/b' is a sub-folder of '/a', and we should only keep '/a' in the final result. The input is a list of strings representing folder paths, and the output should be a list of strings containing only the parent folders (i.e., folders that are not sub-folders of any other folder in the input).
Solution Approach
The solution utilizes a Trie (prefix tree) data structure to store the folder paths and efficiently identify sub-folders. A Trie is built by inserting each folder path. Then, a Breadth-First Search (BFS) is used to traverse the Trie. While traversing, if a node represents the end of a folder path, it's considered a parent folder. Sub-folders are identified because once a parent folder is found during the BFS, we stop exploring further down that branch, effectively pruning the sub-folders.
Step-by-Step Algorithm
- Step 1: Initialize an empty Trie (implemented using nested dictionaries or HashMaps).
- Step 2: Iterate through the input `folder` list and insert each `path` into the Trie. Each level of the Trie represents a directory segment (e.g., 'a', 'b', 'c' in '/a/b/c'). A special marker ('$' in Python/Java or a boolean flag in C) is used to indicate the end of a path and store the full folder string at that node.
- Step 3: Initialize an empty list `res` to store the parent folder paths.
- Step 4: Perform a Breadth-First Search (BFS) on the Trie, starting from the root.
- Step 5: In each BFS iteration, retrieve a node from the queue.
- Step 6: Check if the current node contains the end-of-path marker. If it does, it means the node represents a parent folder.
- Step 7: If the node represents a parent folder, add the corresponding folder path to the `res` list and skip exploring the children of that node. This effectively removes any sub-folders of the parent folder from consideration.
- Step 8: If the node does not represent a parent folder, add its child nodes to the BFS queue to continue the traversal.
- Step 9: After the BFS traversal is complete, return the `res` list, which contains the parent folder paths.
Key Insights
- Insight 1: Sorting the folder paths by length allows us to efficiently check for sub-folders. A shorter path can only be a parent of a longer path, not the other way around.
- Insight 2: Using a Trie data structure helps in quickly checking if a path is a sub-folder of any existing path.
- Insight 3: Iterating through the folders and checking for sub-folders in a naive way (string prefix matching) can be inefficient; a Trie enables more efficient prefix checks.
Complexity Analysis
Time Complexity: O(n*l)
Space Complexity: O(n*l)
Topics
This problem involves: Array, String, Depth-First Search, Trie.
Companies
Asked at: Snowflake, Verkada.