[Leetcode] 609. Find Duplicate File in System 解题报告

本文介绍了一种算法,用于在文件系统中查找具有相同内容的文件组,并讨论了在真实文件系统中搜索文件的不同策略及其优化方法。

题目

Given a list of directory info including directory path, and all the files with contents in this directory, you need to find out all the groups of duplicate files in the file system in terms of their paths.

A group of duplicate files consists of at least two files that have exactly the same content.

A single directory info string in the input list has the following format:

"root/d1/d2/.../dm f1.txt(f1_content) f2.txt(f2_content) ... fn.txt(fn_content)"

It means there are n files (f1.txtf2.txt ... fn.txt with content f1_contentf2_content ... fn_content, respectively) in directory root/d1/d2/.../dm. Note that n >= 1 and m >= 0. If m = 0, it means the directory is just the root directory.

The output is a list of group of duplicate file paths. For each group, it contains all the file paths of the files that have the same content. A file path is a string that has the following format:

"directory_path/file_name.txt"

Example 1:

Input:
["root/a 1.txt(abcd) 2.txt(efgh)", "root/c 3.txt(abcd)", "root/c/d 4.txt(efgh)", "root 4.txt(efgh)"]
Output:  
[["root/a/2.txt","root/c/d/4.txt","root/4.txt"],["root/a/1.txt","root/c/3.txt"]]

Note:

  1. No order is required for the final output.
  2. You may assume the directory name, file name and file content only has letters and digits, and the length of file content is in the range of [1,50].
  3. The number of files given is in the range of [1,20000].
  4. You may assume no files or directories share the same name in the same directory.
  5. You may assume each given directory info represents a unique directory. Directory path and file info are separated by a single blank space.

Follow-up beyond contest:
  1. Imagine you are given a real file system, how will you search files? DFS or BFS?
  2. If the file content is very large (GB level), how will you modify your solution?
  3. If you can only read the file by 1kb each time, how will you modify your solution?
  4. What is the time complexity of your modified solution? What is the most time-consuming part and memory consuming part of it? How to optimize?
  5. How to make sure the duplicated files you find are not false positive?

思路

算法上的难度不大,我觉得主要在于如何很好地回答follow up问题。所以我首先简单地总结一下思路,然后重点探讨如何回答follow up(自己的回答不一定准确,仅供参考)。

算法上关键还是在于建立一个哈希表,建立从fn_content到文件全路径(folder_path + "/" + fn)数组之间的映射。对于paths中的每一个string,我们用istringstream进行解析,其第一个string就是folder_name,之后的每个string就是该文件夹下面的一个fn + fn_content。解析出来之后,再进一步解析fn和fn_content,然后将对应数据加入哈希表中。最后遍历一遍哈希表,找出同样内容出现在多个文件中的项目,加入结果集中即可。

关于follow-up:

1)如果是一个真实的文件系统,那么其实DFS和BFS搜索都是可以的。不同点在于用DFS搜索时,如果文件深度太深,那么可能会需要占用较大的内存栈空间;用BFS时,如果每个文件夹下面的文件夹数量太多,则队列可能会比较长,因此也会占用较大的内存空间。

2)如果文件内容过大,那么显然就不能将文件内容直接作为哈希表的key了。我觉得有几种可能的改进方法:a)用MD5算法(或者其它类似的算法)生成文件内容对应的key,然后用这个key作为哈希表的key;2)采用多次哈希的方法,例如第一次用fn_content的长度来作为key,第二次处理的时候,在长度相同的文件列表中,再用MD5算法判断文件内容是不是相同。这样做的好处在于第一次哈希的时候,就排除了大量内容大小不同的文件,从而使得需要用MD5算法的文件数目大大减少,从而提高效率。

3)那就每读进来1kb的数据,做一次MD5吧,最后把所有的MD5结果生成一个向量,或者再次MD5,用最终结果来作为哈希表的key。

4)如果认为平均每个文件夹下面有常数个文件(夹),那么修改后的算法的时间复杂度就是O(file_counts * avg_file_length),其中file_counts表示文件系统中所有的文件个数;avg_file_length表示平均文件长度。最耗时的有可能是两部分:a)遍历文件的部分(如果文件数量特别大);b)读取每个文件内容的部分(如果每个文件特别大)。要优化,那么除了采用3)中的方法之外,还可以采用并行化方法来处理。例如遍历文件的时候,采用多线程或者map reduce的思路进行并行,最后再合成所有的结果。读取每个文件内容的时候,也可以采用多线程(需要硬盘支持了,有这样的硬盘吗?)。

5)再牛逼的哈希算法也不能保证完全消除冲突,那么为了make sure,就只好最后对结果采用最笨的方法进行double check了。。。

代码

class Solution {
public:
    vector<vector<string>> findDuplicate(vector<string>& paths) {
        unordered_map<string, vector<string>> hash;     // {fn_content, fn_path}
        for (int i = 0; i < paths.size(); ++i) {
            string folder_path;
            vector<string> files;
            resolve(paths[i], folder_path, files);
            for (int j = 0; j < files.size(); ++j) {
                string name, content;
                getNameContent(files[j], name, content);
                string path = folder_path + "/" + name;
                hash[content].push_back(path);
            }
        }
        vector<vector<string>> ret;
        for (auto it = hash.begin(); it != hash.end(); ++it) {
            if (it->second.size() > 1) {
                ret.push_back(it->second);
            }
        }
        return ret;
    }
private:
    void resolve(const string &s, string &folder_path, vector<string> &files) {
        istringstream iss(s);
        iss >> folder_path;
        string file;
        while (!iss.eof()) {
            iss >> file;
            files.push_back(file);
        }
    }
    void getNameContent(const string &file, string &name, string &content) {
        int index = file.find('(');
        name = file.substr(0, index);
        content = file.substr(index);
    }
};
### 如何在 VSCode 中安装和配置 LeetCode 插件以及 Node.js 运行环境 #### 安装 LeetCode 插件 在 VSCode 的扩展市场中搜索 `leetcode`,找到官方提供的插件并点击 **Install** 按钮进行安装[^1]。如果已经安装过该插件,则无需重复操作。 #### 下载与安装 Node.js 由于 LeetCode 插件依赖于 Node.js 环境,因此需要下载并安装 Node.js。访问官方网站 https://nodejs.org/en/ 并选择适合当前系统的版本(推荐使用 LTS 版本)。按照向导完成安装流程后,需确认 Node.js 是否成功安装到系统环境中[^2]。 可以通过命令行运行以下代码来验证: ```bash node -v npm -v ``` 上述命令应返回对应的 Node.js 和 npm 的版本号。如果没有正常返回版本信息,则可能未正确配置环境变量。 #### 解决环境路径问题 即使完成了 Node.js 的安装,仍可能出现类似 “LeetCode extension needs Node.js installed in environment path” 或者 “command ‘leetcode.toggleLeetCodeCn’ not found” 的错误提示[^3]。这通常是因为 VSCode 未能识别全局的 Node.js 路径或者本地安装的 nvm 默认版本未被正确加载[^4]。 解决方法如下: 1. 手动指定 Node.js 可执行文件的位置 在 VSCode 设置界面中输入关键词 `leetcode`,定位至选项 **Node Path**,将其值设为实际的 Node.js 安装目录下的 `node.exe` 文件位置。例如:`C:\Program Files\nodejs\node.exe`。 2. 使用 NVM 用户管理工具调整默认版本 如果通过 nvm 工具切换了不同的 Node.js 版本,请确保设置了默认使用的版本号。可通过以下指令实现: ```bash nvm alias default <version> ``` 重新启动 VSCode 后测试功能键是否恢复正常工作状态。 --- #### 配置常用刷题语言 最后一步是在 VSCode 设置面板中的 LeetCode 插件部分定义个人习惯采用的主要编程语言作为默认提交方式之一。这样可以减少频繁修改编码风格的时间成本。 --- ### 总结 综上所述,要在 VSCode 上顺利启用 LeetCode 插件及其关联服务,除了基本插件本身外还需额外准备支持性的后台框架——即 Node.js 应用程序引擎;同时针对特定场景下产生的兼容性障碍采取针对性措施加以修正即可达成目标[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值