【字典树(前缀树) 哈希映射 后序序列化】1948. 删除系统中的重复文件夹

本文涉及知识点

字典树(前缀树) 哈希映射 后序序列化

LeetCode 1948. 删除系统中的重复文件夹

由于一个漏洞,文件系统中存在许多重复文件夹。给你一个二维数组 paths,其中 paths[i] 是一个表示文件系统中第 i 个文件夹的绝对路径的数组。
例如,[“one”, “two”, “three”] 表示路径 “/one/two/three” 。
如果两个文件夹(不需要在同一层级)包含 非空且相同的 子文件夹 集合 并具有相同的子文件夹结构,则认为这两个文件夹是相同文件夹。相同文件夹的根层级 不 需要相同。如果存在两个(或两个以上)相同 文件夹,则需要将这些文件夹和所有它们的子文件夹 标记 为待删除。

例如,下面文件结构中的文件夹 “/a” 和 “/b” 相同。它们(以及它们的子文件夹)应该被 全部 标记为待删除:
/a
/a/x
/a/x/y
/a/z
/b
/b/x
/b/x/y
/b/z
然而,如果文件结构中还包含路径 “/b/w” ,那么文件夹 “/a” 和 “/b” 就不相同。注意,即便添加了新的文件夹 “/b/w” ,仍然认为 “/a/x” 和 “/b/x” 相同。
一旦所有的相同文件夹和它们的子文件夹都被标记为待删除,文件系统将会 删除 所有上述文件夹。文件系统只会执行一次删除操作。执行完这一次删除操作后,不会删除新出现的相同文件夹。
返回二维数组 ans ,该数组包含删除所有标记文件夹之后剩余文件夹的路径。路径可以按 任意顺序 返回。
示例 1:
在这里插入图片描述

输入:paths = [[“a”],[“c”],[“d”],[“a”,“b”],[“c”,“b”],[“d”,“a”]]
输出:[[“d”],[“d”,“a”]]
解释:文件结构如上所示。
文件夹 “/a” 和 “/c”(以及它们的子文件夹)都会被标记为待删除,因为它们都包含名为 “b” 的空文件夹。
示例 2:

在这里插入图片描述

输入:paths = [[“a”],[“c”],[“a”,“b”],[“c”,“b”],[“a”,“b”,“x”],[“a”,“b”,“x”,“y”],[“w”],[“w”,“y”]]
输出:[[“c”],[“c”,“b”],[“a”],[“a”,“b”]]
解释:文件结构如上所示。
文件夹 “/a/b/x” 和 “/w”(以及它们的子文件夹)都会被标记为待删除,因为它们都包含名为 “y” 的空文件夹。
注意,文件夹 “/a” 和 “/c” 在删除后变为相同文件夹,但这两个文件夹不会被删除,因为删除只会进行一次,且它们没有在删除前被标记。
示例 3:
在这里插入图片描述

输入:paths = [[“a”,“b”],[“c”,“d”],[“c”],[“a”]]
输出:[[“c”],[“c”,“d”],[“a”],[“a”,“b”]]
解释:文件系统中所有文件夹互不相同。
注意,返回的数组可以按不同顺序返回文件夹路径,因为题目对顺序没有要求。
示例 4:
在这里插入图片描述

输入:paths = [[“a”],[“a”,“x”],[“a”,“x”,“y”],[“a”,“z”],[“b”],[“b”,“x”],[“b”,“x”,“y”],[“b”,“z”]]
输出:[]
解释:文件结构如上所示。
文件夹 “/a/x” 和 “/b/x”(以及它们的子文件夹)都会被标记为待删除,因为它们都包含名为 “y” 的空文件夹。
文件夹 “/a” 和 “/b”(以及它们的子文件夹)都会被标记为待删除,因为它们都包含一个名为 “z” 的空文件夹以及上面提到的文件夹 “x” 。
示例 5:
在这里插入图片描述

输入:paths = [[“a”],[“a”,“x”],[“a”,“x”,“y”],[“a”,“z”],[“b”],[“b”,“x”],[“b”,“x”,“y”],[“b”,“z”],[“b”,“w”]]
输出:[[“b”],[“b”,“w”],[“b”,“z”],[“a”],[“a”,“z”]]
解释:本例与上例的结构基本相同,除了新增 “/b/w” 文件夹。
文件夹 “/a/x” 和 “/b/x” 仍然会被标记,但 “/a” 和 “/b” 不再被标记,因为 “/b” 中有名为 “w” 的空文件夹而 “/a” 没有。
注意,“/a/z” 和 “/b/z” 不会被标记,因为相同子文件夹的集合必须是非空集合,但这两个文件夹都是空的。

提示:

1 <= paths.length <= 2 * 104
1 <= paths[i].length <= 500
1 <= paths[i][j].length <= 10
1 <= sum(paths[i][j].length) <= 2 * 105
path[i][j] 由小写英文字母组成
不会存在两个路径都指向同一个文件夹的情况
对于不在根层级的任意文件夹,其父文件夹也会包含在输入中

字典树

如何判断两个文件夹是否相等? 除子树根节点外的序列化是否相等。后序序列化更简单。序列化完子孙后就比较。
分三步:
一,将所有path放到字典树中。字典树的节点包括path[i][j],i。
二,DFS字典树,将各路径不包括当前节点的后序序列化放到哈希映射m_mSer中,并记录数量。注意:序列时,要排序。不如:直接用有序映射。
三,DFS字典树,看各节点是否有相等的哈希,标记此节点需要删除。
四,复制不需要删除的节点到vRet中。

复杂度

主要复杂度在序列化。
∀ i ∀ j \forall i\forall j ij path[i][i]的层次是leve(从0开始),则它序列化leve+1次,它会被它及它的祖先序列化。
它及它的祖先分别被序列化: leve+1 ,leve ⋯ \cdots 1 次
它及它的祖先分别在path出现的次数: 1 ,2 ⋯ \cdots leve+1 次。
次数相同,等于 path[i][j] 元素的个数,我们假设最极端情况下: ∀ i ∀ j \forall i\forall j ij path[i][j].length==1。令o=sum(path[i][j].lenght()) ,则 ∑ \sum path[i][j]序列的次数 <= o。再次假设极端情况下: ∀ i ∀ j \forall i\forall j ij path[i][j] == 10。 则序列化的空间复制度和时间复杂度等于O(o × \times × 10) ≈ \approx 2 × \times × 106

代码

核心代码

class CStrToIndex
{
public:CStrToIndex() {}CStrToIndex(const vector<string>& wordList) {for (const auto& str : wordList){Add(str);}}int Add(const string& str){if (m_mIndexs.count(str)) { return m_mIndexs[str]; }m_mIndexs[str] = m_strs.size();m_strs.push_back(str);return  m_strs.size()-1;}vector<string> m_strs;int GetIndex(const string& str){if (m_mIndexs.count(str)) { return m_mIndexs[str]; }return -1;}
protected:unordered_map<string, int> m_mIndexs;
};class CPathTrieNode
{
public:CPathTrieNode* Add(string str) {if (!m_mChild.count(str)) {m_mChild[str] = new CPathTrieNode();}return m_mChild[str];}int m_iSerIndex =-1;map<string, CPathTrieNode*> m_mChild;
};class CPathTrie {
public:void Add(const vector<string>& path) {auto ptr = &m_root;for (auto& s : path) {ptr = ptr->Add(s);}}CPathTrieNode m_root;
};
class Solution {
public:vector<vector<string>> deleteDuplicateFolder(vector<vector<string>>& paths) {CPathTrie trie;for (const auto& path : paths) {trie.Add(path);}DFSForSer("", &trie.m_root);vector<string> stack;DFS(stack, &trie.m_root);return m_vRet;}string DFSForSer (string strName,CPathTrieNode* cur) {string str;for (const auto& [tmp, child] : cur->m_mChild) {str += "(" + DFSForSer(tmp,child) + ")";}if ("" != str)	{cur->m_iSerIndex = m_strIndex.Add(str);	m_mIndexCount[cur->m_iSerIndex]++;}str += strName;return str ;};void DFS(vector<string>& stack, CPathTrieNode* cur){for (const auto& [tmp, child] : cur->m_mChild) {if ((-1 != child->m_iSerIndex) && (m_mIndexCount[child->m_iSerIndex] > 1)) {continue;}stack.emplace_back(tmp);m_vRet.emplace_back(stack);DFS(stack, child);stack.pop_back();}}vector<vector<string>> m_vRet;unordered_map<int, int> m_mIndexCount;CStrToIndex m_strIndex;
};

VS自带的单元测试

template<class T1,class T2>
void AssertEx(const T1& t1, const T2& t2)
{Assert::AreEqual(t1 , t2);
}template<class T>
void AssertEx(const vector<T>& v1, const vector<T>& v2)
{Assert::AreEqual(v1.size(), v2.size());	for (int i = 0; i < v1.size(); i++){Assert::AreEqual(v1[i], v2[i]);}
}template<class T>
void AssertV2(vector<vector<T>> vv1, vector<vector<T>> vv2)
{sort(vv1.begin(), vv1.end());sort(vv2.begin(), vv2.end());Assert::AreEqual(vv1.size(), vv2.size());for (int i = 0; i < vv1.size(); i++){AssertEx(vv1[i], vv2[i]);}
}namespace UnitTest
{vector<vector<string>> paths;TEST_CLASS(UnitTest){public:TEST_METHOD(TestMethod1){paths = { {"a"},{"c"},{"d"},{"a","b"},{"c","b"},{"d","a"} };auto res = Solution().deleteDuplicateFolder(paths);			AssertV2(res,{ {"d"},{"d","a"} });}TEST_METHOD(TestMethod2){paths = { {"a"},{"c"},{"a","b"},{"c","b"},{"a","b","x"},{"a","b","x","y"},{"w"},{"w","y"} };auto res = Solution().deleteDuplicateFolder(paths);AssertV2(res, { {"c"},{"c","b"},{"a"},{"a","b"} });}TEST_METHOD(TestMethod3){paths = { {"a","b"},{"c","d"},{"c"},{"a"} };auto res = Solution().deleteDuplicateFolder(paths);AssertV2(res, { {"c"},{"c","d"},{"a"},{"a","b"} });}TEST_METHOD(TestMethod4){paths = { {"a"},{"a","x"},{"a","x","y"},{"a","z"},{"b"},{"b","x"},{"b","x","y"},{"b","z"} };auto res = Solution().deleteDuplicateFolder(paths);AssertV2(res, {  });}TEST_METHOD(TestMethod5){paths = { {"a"},{"a","x"},{"a","x","y"},{"a","z"},{"b"},{"b","x"},{"b","x","y"},{"b","z"},{"b","w"} };auto res = Solution().deleteDuplicateFolder(paths);AssertV2(res, { {"b"},{"b","w"},{"b","z"},{"a"},{"a","z"} });}};
}

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
《喜缺全书算法册》以原理、正确性证明、总结为主。
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/19652.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

13.优化界面化的游戏辅助

12.使用mfc实现游戏辅助的界面 在它的代码上进行修改 12.使用mfc实现游戏辅助的界面它的代码是频繁读写游戏的内存&#xff0c;这样不是很好&#xff0c;下面的代码是在它的基础上进行了封装&#xff0c;控制无敌的逻辑在我们申请的内存中实现&#xff08;也就是在一个全局中实…

小程序项目创建与Vant-UI引入

一&#xff0c;创建小程序项目 AppID可先用测试号&#xff1b; 模板来源选择 ’全部来源‘ &#xff0c;’基础‘ 。模板一定JS开头的&#xff1b; vant-weapp 官网 vant-Weapp 二&#xff0c;下载vant-weapp 组件 1&#xff0c;在新项目中打开 ’调试器‘&#xff1b; 2…

前端Vue小兔鲜儿电商项目实战Day03

一、Home - 整体结构搭建和分类实现 1. 页面结构 ①按照结构新增5个组件&#xff0c;准备最简单的模板&#xff0c;分别在Home模块的入口组件中引入 src/views/Home/components/ HomeCategory.vue HomeBanner.vue HomeNew.vue HomeHot.vue HomeProduct.vue <script …

COMSOL中液晶材料光学特性模拟

前面我们根据FDTD官方文档设置了液晶指向的模型。COMSOL也可以根据相似的方法设置各项异性的周期性变化的材料。 该方法参考了luneburg_lens的COMSOL文档 在给出的文件中&#xff0c;可以发现定义-变量中可以使用默认坐标作为变量&#xff0c;即xyz。因此&#xff0c;折射率也可…

利用依赖结构矩阵管理架构债务

本文讨论了如何利用依赖结构矩阵&#xff08;DSM&#xff0c;Dependency Structure Matrix&#xff09;管理和识别架构债务&#xff0c;并通过示例应用展示了这一过程。原文: Managing Architecture Debt with Dependency Structure Matrix Vlado Paunovic Unsplash 技术债务&a…

Java学习18——集合

目录 一.集合 1.集合的优点&#xff1a; 2.集合的框架体系&#xff1a; &#xff08;1&#xff09;单列集合&#xff1a; &#xff08;2&#xff09;双列集合&#xff08;key&#xff0c;value&#xff09;&#xff1a; 3.Collection接口和常用方法&#xff1a; 4.迭代器遍…

计算机图形学入门05:投影变换

1.投影变换 上一章已经介绍了投影变换&#xff0c;就是将三维图像投影到二维平面上&#xff0c;而投影变换又分为正交投影(Orthographic Projection)和透视投影(Perspective Projection)。如下图&#xff1a; 正交投影 没有近大远小的现象&#xff0c;无论图形与视点距离是远是…

977. 有序数组的平方 - 力扣

1. 题目 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 2. 示例 3. 分析 我们当然可以遍历数组平方元素&#xff0c;然后再使用sort排序&#xff0c;但这里时间复杂度就为 O(logN) 了。 我…

c语言基础:数组的运用以及在内存中的地址的理解

目录 目录&#xff1a; 1.数组作为函数参数 2.数组在内存中的存储 2.1数组名是什么&#xff1f; 2.2下面我们来探讨二维数组的各个名字表示什么 二维数组的首元素地址是什么呢&#xff1f; *arr表示的是什么呢 &#xff1f;&#xff08;arr是二维数组&#xff09; 1.数组作…

【小白专用24.5.30已验证】Composer安装php框架thinkPHP6的安装教程

一、框架介绍 1、框架简介和版本选择 Thinkphp是一种基于php的开源web应用程序开发框架ThinkPHP框架&#xff0c;是免费开源的、轻量级的、简单快速且敏捷的php框架。你可以免费使用TP框架&#xff0c;甚至可以将你的项目商用&#xff1b; ThinkPHP8.0 是目前框架正式版的最新版…

全球高端奢侈瑜伽服市场霸主Lululemon在美股的股价还有巨大的上涨空间

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 一、Lululemon公司介绍 1.1、创立与起源&#xff1a; Lululemon(LULU)由Chip Wilson于1998年在加拿大温哥华创立。1.2、产品与市场定位&#xff1a; Lululemon起初以瑜伽裤起家&#xff0c;现已发展成为涵盖瑜伽、跑步、训…

越来越多企业选择开源批发订货系统

在当今竞争激烈的市场环境中&#xff0c;越来越多的企业选择开源批发订货系统来提高运营效率、降低成本并实现业务的数字化转型。以下是开源批发订货系统的四大优势及其重要功能&#xff1a; 首先&#xff0c;开源批发订货系统具有高度的灵活性和定制性。由于其源代码开放&…

打造高效上传体验:基于Kotlin的Android快速上传框架

1. 引言 在Android开发中&#xff0c;文件上传操作常常面临各种挑战&#xff0c;为此我开源了一个高效、易用的快速上传框架&#xff0c;助力开发者轻松实现文件上传功能。 GitHub项目地址: 点我 2. 框架特点概述 纯Kotlin编写&#xff1a;简洁、现代的编程语言。MVVM架构&a…

提升船舶安全性与效率:隔离驱动芯片的应用

随着科技的不断发展&#xff0c;船舶行业也在不断迎来新的技术革新&#xff0c;其中隔离驱动芯片作为一种关键的电子元件&#xff0c;在船舶领域发挥着重要作用。本文将深入探讨隔离驱动芯片在船舶领域的应用及其技术特点。 隔离驱动芯片提升船舶系统安全性 船舶作为大型交通工…

C盘文件被格式化了,要怎么恢复?

C盘通常是操作系统(如Windows)的默认安装目录。它包含了操作系统的核心文件、驱动程序及系统所需的各种支持文件。这些文件对于计算机的正常运行至关重要。但在使用的过程中&#xff0c;有时可能会因为各种原因导致C盘被格式化&#xff0c;从而丢失了这些重要文件。这无疑是一个…

模型构建器之迭代器

上一篇我们介绍了模型构建器的基础&#xff0c;将一个工作流串联起来&#xff0c;然后做成模型工具。今天我们介绍模型构建器的第二个重要功能——迭代&#xff0c;也就是程序中的循环。 先来看一个例子。要给数据库中所有要素类添加一个相同的字段&#xff0c;该怎么做&#…

语音深度鉴伪识别项目实战:基于深度学习的语音深度鉴伪识别算法模型(二)音频数据预处理及去噪算法+Python源码应用

前言 深度学习技术在当今技术市场上面尚有余力和开发空间的&#xff0c;主流落地领域主要有&#xff1a;视觉&#xff0c;听觉&#xff0c;AIGC这三大板块。 目前视觉板块的框架和主流技术在我上一篇基于Yolov7-LPRNet的动态车牌目标识别算法模型已有较为详细的解说。与AIGC相…

2024年5月架构试题

2024年5月份架构师考试真题完整版 截至2024-5-28 19:24:14已全部收录完成 共75道选择题&#xff0c;5道案例题&#xff0c;4道论文题。题目顺序不分先后。 全网最全的2024年5月份架构师考试真题回忆版&#xff0c;包含答案和解析。 选择题 计算机基础 操作系统调度算法 选先来先…

【Linux基础】Linux了解、安装centos虚拟机

【Linux基础】Linux了解、安装centos虚拟机 文章目录 【Linux基础】Linux了解、安装centos虚拟机1、什么是Linux2、Linux安装2.1、使用VMware安装Linux centos72.2、启动虚拟机安装 1、什么是Linux Linux是一套免费使用和自由传播的操作系统。说到操作系统&#xff0c;大家比较…

vscode 远程连接出现问题

终端太小了&#xff0c; 因为终端中有换行符&#xff0c;如果 终端太小会出现问题