来源:力扣(LeetCode)

描述:

你是一位系统管理员,手里有一份文件夹列表 folder,你的任务是要删除该列表中的所有 子文件夹,并以 任意顺序 返回剩下的文件夹。

如果文件夹 folder[i] 位于另一个文件夹 folder[j] 下,那么 folder[i] 就是 folder[j]子文件夹

文件夹的「路径」是由一个或多个按以下格式串联形成的字符串:'/' 后跟一个或者多个小写英文字母。

  • 例如,"/leetcode""/leetcode/problems" 都是有效的路径,而空字符串和 "/" 不是。

示例 1:

输入:folder = ["/a","/a/b","/c/d","/c/d/e","/c/f"]
输出:["/a","/c/d","/c/f"]
解释:"/a/b" 是 "/a" 的子文件夹,而 "/c/d/e" 是 "/c/d" 的子文件夹。

示例 2:

输入:folder = ["/a","/a/b/c","/a/b/d"]
输出:["/a"]
解释:文件夹 "/a/b/c" 和 "/a/b/d" 都会被删除,因为它们都是 "/a" 的子文件夹。

示例 3:

输入: folder = ["/a/b/c","/a/b/ca","/a/b/d"]
输出: ["/a/b/c","/a/b/ca","/a/b/d"]

提示:

  • 1 <= folder.length <= 4 * 104
  • 2 <= folder[i].length <= 100
  • folder[i] 只包含小写字母和 ‘/’
  • folder[i] 总是以字符 ‘/’ 起始
  • 每个文件夹名都是 唯一 的

方法一:排序

思路与算法

  我们可以将字符串数组 folder 按照字典序进行排序。在排序完成后,对于每一个 folder[i],如果 folder[i−1] 恰好是它的前缀,并且 folder[i] 第一个多出的字符是 /,那么我们就可以把 folder[i] 删除。

注意当 folder[i] 被删除后,后续的所有字符串都需要向前移动一个位置。例如 [“/a”,“/a/b”,“/a/c”] 中,“/a/b” 被删除后,数组会变为 [“/a”,“/a/c”],“/a/c” 也会被删除。

  这样做的必要性是显然的,因为如果上述条件满足,就说明 folder[i] 是 folder[i−1] 的子文件夹。对于充分性,我们可以使用反证法:

假设 folder[i] 是某个 folder[j] (j ≠ i − 1) 的子文件夹但不是 folder[i − 1] 的子文件夹,那么在排序后,folder[j] 一定出现在 folder[i] 的前面,也就是有 j < i。如果有多个满足要求的 j,我们选择最早出现的那个。这样就保证了 folder[j] 本身不会是其它文件夹的子文件夹。
由于 “/” 的字典序小于所有的小写字母,并且 folder[i] 是由 folder[j] 加上 “/” 再加上后续字符组成,因此在 folder[i] 和 folder[j] 之间的所有字符串也都一定是由 folder[j] 加上 “/” 再加上后续字符组成。这些字符串都是 folder[i] 的子文件夹,它们会依次被删除。当遍历到 folder[i] 时,它的上一个元素恰好是 folder[j],因此它一定会被删除。

代码:

class Solution {public:vector<string> removeSubfolders(vector<string>& folder) {sort(folder.begin(), folder.end());vector<string> ans = {folder[0]};for (int i = 1; i < folder.size(); ++i) {if (int pre = ans.end()[-1].size(); !(pre < folder[i].size() && ans.end()[-1] == folder[i].substr(0, pre) && folder[i][pre] == '/')) {ans.push_back(folder[i]);}}return ans;}
};

执行用时:132 ms, 在所有 C++ 提交中击败了88.46%的用户
内存消耗:40 MB, 在所有 C++ 提交中击败了82.31%的用户
复杂度分析
时间复杂度:O(nl⋅logn),其中 n 和 l 分别是数组 folder 的长度和文件夹的平均长度。O(nl⋅logn) 为排序需要的时间,后续构造答案需要的时间为 O(nl),在渐进意义下小于前者。
空间复杂度:O(l)。在构造答案比较前缀时,我们使用了字符串的截取子串操作,因此需要 O(l) 的临时空间。我们也可以使用一个递增的指针依次对两个字符串的每个相同位置进行比较,省去这一部分的空间,使得空间复杂度降低至排序需要的栈空间 O(logn)。但空间优化并不是本题的重点,因此上述的代码中仍然采用空间复杂度为 O(l) 的写法。注意这里不计入返回值占用的空间。

方法二:字典树

思路与算法

  我们也可以使用字典树来解决本题。文件夹的拓扑结构正好是树形结构,即字典树上的每一个节点就是一个文件夹。

  对于字典树中的每一个节点,我们仅需要存储一个变量 ref,如果 ref ≥ 0,说明该节点对应着 folder[ref ],否则(ref = −1)说明该节点只是一个中间节点。

  我们首先将每一个文件夹按照 “/” 进行分割,作为一条路径加入字典树中。随后我们对字典树进行一次深度优先搜索,搜索的过程中,如果我们走到了一个 ref ≥ 0 的节点,就将其加入答案,并且可以直接回溯,因为后续(更深的)所有节点都是该节点的子文件夹。

代码:

struct Trie {Trie(): ref(-1) {}unordered_map<string, Trie*> children;int ref;
};class Solution {public:vector<string> removeSubfolders(vector<string>& folder) {auto split = [](const string& s) -> vector<string> {vector<string> ret;string cur;for (char ch: s) {if (ch == '/') {ret.push_back(move(cur));cur.clear();}else {cur.push_back(ch);}}ret.push_back(move(cur));return ret;};Trie* root = new Trie();for (int i = 0; i < folder.size(); ++i) {vector<string> path = split(folder[i]);Trie* cur = root;for (const string& name: path) {if (!cur->children.count(name)) {cur->children[name] = new Trie();}cur = cur->children[name];}cur->ref = i;}vector<string> ans;function<void(Trie*)> dfs = [&](Trie* cur) {if (cur->ref != -1) {ans.push_back(folder[cur->ref]);return;}for (auto&& [_, child]: cur->children) {dfs(child);}};dfs(root);return ans;}
};

执行用时:356 ms, 在所有 C++ 提交中击败了17.69%的用户
内存消耗:174.3 MB, 在所有 C++ 提交中击败了11.54%的用户
复杂度分析
时间复杂度:O(nl),其中 n 和 l 分别是数组 folder 的长度和文件夹的平均长度。即为构造字典树和答案需要的时间。
空间复杂度:O(nl),即为字典树需要使用的空间。注意这里不计入返回值占用的空间。
author:LeetCode-Solution

【1233. 删除子文件夹】相关推荐

  1. Leetcode 1233 删除子文件夹 (C++题解)

    你是一位系统管理员,手里有一份文件夹列表 folder,你的任务是要删除该列表中的所有 子文件夹,并以 任意顺序 返回剩下的文件夹. 我们这样定义「子文件夹」: 如果文件夹 folder[i] 位于另 ...

  2. 1233. 删除子文件夹(中等)

    题目描述 你是一位系统管理员,手里有一份文件夹列表 folder,你的任务是要删除该列表中的所有 子文件夹,并以 任意顺序 返回剩下的文件夹. 如果文件夹 folder[i] 位于另一个文件夹 fol ...

  3. 删除子文件夹[字典树 + go变量 + strings.builder的copyCheck()]

    字典树+go变量特定+no Copy机制 前言 一.删除子文件夹 二.字典树 1.idea 2.go 总结 参考文献 前言 对于类似字符串匹配相关的问题,用字典树可以大大提高效率:go的变量没有引用的 ...

  4. python os.removedirs() 和shutil.rmtree()(os.removedirs用于删除非空文件夹和子文件夹、shutil.rmtree用于删除文件夹下所有文件夹和文件)

    文章目录 shutil.rmtree() os.removedirs() shutil.rmtree() shutil.rmtree() 表示递归删除文件夹下的所有子文件夹和子文件. def rmtr ...

  5. asp.net 递归删除文件夹及其子文件夹和所有文件[转]

    删除某文件夹及其所有子文件夹和文件 C#代码 /// <summary> /// 用递归方法删除文件夹目录及文件 /// </summary> /// <param na ...

  6. 删除文件夹及其子文件夹和文件

    删除文件夹及其子文件夹和文件 一.首先创建几个文件夹和文件 package com.inspire;import java.io.File; import java.io.FileOutputStre ...

  7. 递归遍历文件及子文件夹下的文件(该代码是复制过来修改过的,如果有侵作者权的话,请作者联系我,立即删除)...

    调用: ListFiles(new DirectoryInfo(source)); /// <summary>/// //递归遍历所有文件包括子文件夹下的文件 并对word excel p ...

  8. redhat 复制文件夹及子文件夹_python文件夹怎么操作呢??(建议详读)

    当前工作目录 >>> import os >>> os.getcwd() 我电脑上的结果为: 'C:Users我的电脑AppDataLocalProgramsPyt ...

  9. python相对路径找不到文件_Python相对路径从子文件夹导入

    首先发布到SO,所以如果我错过了一些细节,请原谅我. 有没有办法使用来自另一个子文件夹的相对路径而不需要通过os修改sys.path?最终这将从cgi网络服务器运行,所以我宁愿远离python.exe ...

最新文章

  1. 区块链时代的拜占庭容错:Tendermint(四)
  2. 如何让网站被百度谷歌快速收录
  3. 如何从ios酷我音乐盒中导出已下载的音乐文件(使用Java编程实现)
  4. 【剑指offer】面试题6:从尾到头打印链表(Java)
  5. 轻量在线人工客服系统 支持多商家+自动适配移动端
  6. 给内部类对象数组属性赋值时报错:Exception in thread main java.lang.NullPointerException...
  7. android中返回刷新,Android intent 传递对象以及返回刷新
  8. Java数组去重的方法
  9. Flutter实战一Flutter聊天应用(十)
  10. 如果不需要CSS隐藏滚动条
  11. 【渝粤教育】国家开放大学2018年春季 8668-22T汽车涂装技术(A) 参考试题
  12. CrashLoopBackOff问题排查
  13. 红酒百科知识(二)_manok_新浪博客
  14. 交叉销售功能介绍-配置
  15. XBee/XBee-Pro ® ZigBee 模块
  16. 童文、李烨:6G的9大挑战
  17. java 中结束程序方法
  18. Java调用so动态库方法
  19. Matlab平滑处理记录
  20. Automating Parallelism

热门文章

  1. Windows Server 2016
  2. 酒吧经营你要知道的:酒吧定位
  3. 近代物理系的高能物理研究现状
  4. 数据接口异常中的错误
  5. 计算机视觉与互动投影
  6. 西米支付:第三方支付的类型
  7. python变量定义模糊_python_基本语法之变量
  8. 计算机操作系统感悟随笔--程序编译连接
  9. 【产品售前】PPT设计之道
  10. 计算机教师线下研修方式与内容,工作坊线下研修活动心得体会