1. 题目与解析
你是一位系统管理员,手里有一份文件夹列表 folder,你的任务是要删除该列表中的所有 子文件夹,并以 任意顺序 返回剩下的文件夹。
如果文件夹 folder[i] 位于另一个文件夹 folder[j] 下,那么 folder[i] 就是 folder[j] 的 子文件夹 。
文件夹的「路径」是由一个或多个按以下格式串联形成的字符串:'/' 后跟一个或者多个小写英文字母。
- 例如,
"/leetcode"和"/leetcode/problems"都是有效的路径,而空字符串和"/"不是。
输入: folder = ["/a","/a/b","/c/d","/c/d/e","/c/f"]
输出: ["/a","/c/d","/c/f"]
解释: "/a/b" 是 "/a" 的子文件夹,而 "/c/d/e" 是 "/c/d" 的子文件夹。
输入: folder = ["/a","/a/b/c","/a/b/d"]
输出: ["/a"]
解释: 文件夹 "/a/b/c" 和 "/a/b/d" 都会被删除,因为它们都是 "/a" 的子文件夹。
输入: folder = ["/a/b/c","/a/b/ca","/a/b/d"]
输出: ["/a/b/c","/a/b/ca","/a/b/d"]
层级信息的统计,很容易让我们想到要使用字典树的数据结构来解决问题。
让我们来梳理一下题目的要求,先确定我们建立字典树结点要使用哪些数据结构:
- 首先,我们要维护的是所有的父文件路径,需要注意的是,当我们移动到文件的过程中路过的文件夹并不是我们统计的范围,我们答案只输出重点文件路径,即树中的叶子结点,为了方便区分,我们可以在节点中增加isLeaf的标记位;
- 另外,在查询某一节点是否有对应的叶子结点时,我们肯定希望能更快速的完成查询过程,因此,使用字典的结构存储叶子结点就能使查询效率更优化;
- 最后,再使用一位存储当前节点的名字,另外还可以按照需求添加其他信息。
有了节点的数据结构,下面就要树立字典树建立的过程:
- 设置一个flg节点;
- 对于某一个file路径,从flg开始遍历,如果中途遇见了isLeaf的节点,就停止这一过程;
- 没有isLeaf节点时,如果已存在子节点,就直接进入,没有的话就新建并且进入;
- 将file对应的叶子节点标记为isLeaf。
之后使用dfs遍历整棵树,输出所有到叶子结点的路径即可。
2. 题解
class Solution {
List<String> ans = new ArrayList<>();
public List<String> removeSubfolders(String[] folder) {
Node flg = new Node(" ");
flg.isFlg = true;
out: for (String f: folder) {
String[] files = f.split("/");
Node nextNode = flg;
for (String file: files) {
if (file == "") {continue;}
if (nextNode.nextNodes.containsKey(file)) {
nextNode = nextNode.nextNodes.get(file);
} else {
nextNode.nextNodes.put(file, new Node(file));
nextNode = nextNode.nextNodes.get(file);
}
if (nextNode.isLeaf) {
continue out;
}
}
nextNode.isLeaf = true;
}
dfs(flg, "");
return ans;
}
void dfs(Node node, String s) {
if (!node.isFlg) {
s += "/" + node.nodeName;
}
if (node.isLeaf) {
ans.add(s);
} else {
for (Map.Entry<String, Node> e: node.nextNodes.entrySet()) {
dfs(e.getValue(), s);
}
}
}
}
class Node {
String nodeName;
boolean isLeaf, isFlg;
Map<String, Node> nextNodes;
public Node(String s) {
nodeName = s;
nextNodes = new HashMap<>();
isLeaf = false;
}
}