Applied Intuition 文件去重技术面试经验 — Coding + Behavioral
本场面试围绕文件内容去重展开,从递归遍历目录、HashMap 分组逐步深入到大文件处理、Streaming Hash、Hash Collision、异常路径和工程优化,同时包含项目经历、技术挑战、Deadline 和 Feedback 等 Behavioral Questions。
📌 Interview Overview
本场面试整体是一轮偏工程化的技术面试,核心 Coding 题目是“根据文件内容找出重复文件”。
题目基础部分主要考察 DFS/递归遍历和 HashMap 分组,但 Follow-up 逐步延伸到文件内容比较、大文件 I/O、文件大小预筛选、Hash Collision、异常处理以及大规模文件扫描等工程问题。
根据材料中的复盘分析,本场整体难度约为 3.5/5,难点主要不在基础算法,而在于能否将一个简单的文件去重问题扩展成更加可靠的工程方案。
👤 Candidate Background
候选人曾在 TotalEnergies 担任 Data Analyst,并在工作中发现数据 pipeline 缺少安全控制。候选人主动研究安全实践、整理风险和解决方案,并向管理层进行汇报,最终推动团队加入 authentication 和 permissions。
候选人还参与过 RiskBot capstone 项目,在项目剩余两周时处理 voice integration 不稳定的问题,并通过重新设计架构、承担额外 coding 和 integration 工作完成最终系统。
🧠 Behavioral Questions
Question 1 — Ownership
"Can you tell me about a time when you took something significant outside of your area of responsibility? Why was it important? And what's the outcome?"
Candidate's Answer
Situation
At TotalEnergies, the candidate discovered that the data pipeline lacked security controls while building supply chain reports.
Task
The candidate needed to address security gaps even though security work was outside the original Data Analyst responsibility.
Action
The candidate researched security practices, created a structured memo with specific risks and solutions, and presented the findings to management.
Result
The team implemented authentication and permissions. The experience also contributed to the candidate's decision to pursue a cybersecurity master's program at CMU.
Question 2 — Deliver Results Under Tight Deadline
"Give me another example of a time you were able to deliver an important project under tight deadline. What sacrifices did you make to meet that deadline? How didn't it impact the final deliverable? What was the outcome?"
Candidate's Answer
Situation
The RiskBot capstone project had two weeks remaining, while the voice integration was still unstable.
Task
The candidate needed to deliver a polished voice-based AI system for the final presentation.
Action
The candidate sacrificed spring break to prototype a new architecture, took on additional coding responsibilities, and accepted the integration risks involved in changing the architecture close to the deadline.
Result
The candidate delivered the system with seamless voice interactions and received positive feedback regarding the clarity of the architecture.
Sacrifices
Personal time, additional responsibilities, and the risk of breaking existing features.
Impact
According to the candidate's description, these sacrifices improved rather than compromised the final deliverable.
💻 Interview Process
Coding — Find Duplicate Files by Content
Problem
Given a starting path, the path may contain files and folders. Implement a method that finds files with exactly the same content and groups them together.
The key requirement is that two files are considered duplicates based on their content, not their filename, path, or file extension.
For example, the directory structure can be represented as:
/a
├── b
│ ├── c.pdf
│ └── d.txt
├── x
│ ├── y.log
│ └── z.pdf
└── unique.png
If c.pdf and z.pdf contain exactly the same data, they should be placed into the same group.
If d.txt and y.log contain the same data, they should also be placed into the same group even though their extensions are different.
Example Output
[
["/a/b/c.pdf", "/a/x/z.pdf"],
["/a/b/d.txt", "/a/x/y.log"],
["/a/unique.png"]
]
材料中的示例将 unique.png 单独放入一个分组,但题目名称是 Duplicate Files,因此实际面试中需要主动确认是否需要保留只有一个文件的分组。
Given Helpers
listFolder(path)
返回当前目录下一层的所有内容。
isFolder(path)
判断当前路径是否为文件夹。
候选人需要实现:
getDuplicateFiles(String path)
负责递归遍历目录并按照文件内容进行分组。
Key Constraints
- 输入路径和输出路径为绝对路径。
listFolder(path)和isFolder(path)等 helper function 不需要自行实现。listFolder(path)只返回当前目录下一层内容。- 需要递归遍历目录树。
- 文件是否重复取决于内容,而不是文件名、路径或扩展名。
- 需要考虑空目录、无效路径、文件读取失败以及大文件等情况。
Candidate's Initial Approach
基础解法从 DFS/递归遍历开始。
如果当前路径是 folder,则调用 listFolder(path) 获取下一层路径并继续递归。
如果当前路径是 file,则读取文件内容并生成用于分组的 key。
最后使用 HashMap:
HashMap<ContentSignature, List<FilePath>>
将具有相同内容签名的文件放入同一个列表。
Improved Approach
更工程化的方案可以分成四步。
Step 1 — DFS 遍历
从 root path 开始递归遍历目录树,收集所有文件。
Step 2 — Size Pre-filter
先按照文件大小进行预分组。
如果两个文件大小不同,那么它们不可能拥有完全相同的内容,因此不需要继续比较。
Step 3 — Streaming Hash
对于大小相同的文件,不直接使用 readAllBytes() 将整个文件加载到内存,而是使用 InputStream 按 chunk 读取文件,并持续更新 MessageDigest。
Step 4 — HashMap Aggregation
将最终的文件内容 hash 作为 key,将文件路径列表作为 value:
HashMap<Hash, List<FilePath>>
如果需要严格保证内容一致,还可以在 size 和 hash 都相同之后,再进行 byte-by-byte comparison。
Complexity
Time Complexity
O(N × M)
其中 N 为文件数量,M 为平均文件大小。
最坏情况下,需要读取每个文件的内容并计算 hash。
Space Complexity
O(N)
主要用于保存文件路径和 HashMap 分组。
如果采用 streaming/chunk-based hashing,单个文件处理过程中额外的读取 buffer 可以控制在 O(1) 级别,而不需要把整个文件加载到内存。
🔍 Follow-up Questions
Follow-up 1 — 文件路径是目录怎么办?
如果当前路径是 folder,就调用 listFolder(path) 获取下一层内容,然后递归处理。
如果当前路径是 file,则进入文件内容处理逻辑。
Follow-up 2 — 如何比较文件内容?
基础方案可以直接读取文件内容,然后使用内容本身作为 HashMap key。
更工程化的方案是计算文件 hash,避免将完整文件内容长期存储在 HashMap 中。
Follow-up 3 — 如果文件很大怎么办?
不能简单使用:
Files.readAllBytes(path)
因为大型文件可能造成较大的内存压力。
更合理的方案是使用 streaming I/O:
InputStream
↓
read fixed-size buffer
↓
MessageDigest.update(buffer)
↓
final hash
这样可以避免一次性把整个文件加载到内存。
Follow-up 4 — 如何减少不必要的 hash 计算?
先比较文件大小。
只有 size 相同的文件才进入下一阶段的 hash 计算。
因为不同大小的文件不可能完全相同。
Follow-up 5 — 如果两个文件 hash 相同,但内容不同怎么办?
Hash collision 在理论上是可能发生的。
如果业务场景对准确性要求较高,可以采用:
File Size
↓
Hash
↓
Byte-by-byte Comparison
只有 size 相同、hash 相同并且 byte-level comparison 也一致,才最终认定两个文件内容完全相同。
Follow-up 6 — 输入规模非常大怎么办?
可以进一步考虑:
- 文件大小预分组
- Streaming Hash
- Metadata Cache
- 并发文件扫描
- 增量计算
- 分布式任务拆分
具体是否需要这些优化,需要根据实际数据规模和系统需求进行权衡。
Follow-up 7 — 空目录怎么办?
如果目录为空,则递归遍历结束并返回空结果,不应该影响其他路径的处理。
Follow-up 8 — 如果路径既不是文件也不是目录怎么办?
可以根据业务需求选择 Skip、Record Error 或 Throw Exception。
如果目标是批量扫描文件,比较稳妥的工程方案是记录错误并继续处理其他有效文件,避免单个异常路径导致整个任务失败。
Follow-up 9 — 如果文件读取失败怎么办?
需要根据业务要求决定是否跳过、记录错误或者终止任务。
在大规模文件扫描场景中,可以记录失败路径和错误原因,同时继续处理其他文件。
Follow-up 10 — 是否返回 unique 文件?
这是一个需要主动向面试官确认的问题。
如果要求返回所有内容分组,则保留:
["/a/unique.png"]
如果要求只返回真正的 duplicate groups,则过滤掉长度为 1 的列表。
🧩 Related LeetCode Questions
- LeetCode 609 — Find Duplicate File in System
- LeetCode 49 — Group Anagrams
- LeetCode 811 — Subdomain Visit Count
- LeetCode 721 — Accounts Merge
- LeetCode 692 — Top K Frequent Words
这些题的共同思路都是将对象转换成稳定的 key,再通过 HashMap 进行聚合。
🧠 Reusable Problem-Solving Framework
Step 1 — 明确分组依据
先确认到底按照 Filename、Path、Size、Content 还是 Hash 进行分组。
Step 2 — 收集基础对象
这里的基础对象是所有文件,因此首先通过 DFS 递归遍历目录树。
Step 3 — 生成 Signature
为每个文件生成稳定的内容签名。
基础版本可以直接使用文件内容。
工程版本可以使用:
Size + Hash
更严格的版本可以进一步进行 byte-level verification。
Step 4 — HashMap 聚合
使用:
Map<Signature, List<Path>>
完成分组。
Step 5 — 根据需求过滤结果
最后确认是否只返回重复文件,或者保留所有分组。
🗣️ English & Communication
本场题目适合使用以下结构进行英文表达。
Clarify the Requirement
"My understanding is that we need to recursively traverse the root path, find all files, and group files with identical content. I also want to clarify whether single-file groups should be included in the result."
Explain the Basic Solution
"I would first traverse the directory tree using DFS. For each file, I would generate a content signature and use a HashMap to group file paths by that signature."
Explain Large File Handling
"For large files, I wouldn't load the entire file into memory. Instead, I would stream the file in fixed-size chunks and update the hash incrementally."
Explain Optimization
"We can first group files by size because files with different sizes cannot have identical content. Then we only hash files within the same size group."
Explain Collision Handling
"A hash collision is theoretically possible, so if correctness is critical, I would perform a byte-level comparison after matching both size and hash."
❌ What Went Wrong
根据材料中的复盘分析,本场主要需要改进的地方包括:
- 基础代码完整度需要加强。
- 对异常路径、空目录和文件读取失败的处理需要更加主动。
- 大文件不能直接全部读入内存,需要提前考虑 streaming I/O。
- Hash 不能在严格准确场景下直接等价于文件内容,需要考虑 collision verification。
- 题目示例中存在 unique file 是否应该返回的潜在歧义,需要主动澄清。
- 解题过程中可以更早使用“DFS → Signature → HashMap”三步法进行表达。
📊 Interview Assessment
Problem Understanding & Clarification
4/5
能够理解核心问题是按照文件内容进行分组,但需要更加主动确认是否返回 unique file。
Problem-Solving & Structured Communication
3.5/5
HashMap 分组方向正确,但可以更加清晰地按照 DFS → Signature → HashMap 三步进行表达。
Technical Correctness
3.5/5
基础算法方向正确,但工程实现需要进一步考虑 hash collision、异常路径和大文件处理。
Complexity & Trade-off Awareness
3/5
能够理解 HashMap 分组的基本复杂度,但需要更加主动比较 readAllBytes()、streaming hash 和 size pre-filter 的取舍。
Engineering & Production Thinking
3/5
这是本场最值得加强的部分,包括大文件、文件读取失败、空目录、权限问题以及大规模扫描等情况。
Communication
4/5
如果能够顺着面试官的 Follow-up 调整方案,并持续解释当前设计的原因,整体沟通表现较为正向。
根据材料中的复盘评分,本场比较值得重点提升的是工程与落地思维,以及复杂度和 Trade-off 表达。
💡 Interviewer Style
根据材料中的复盘分析,面试官的提问方式偏向技术深挖和工程场景追问。
问题从基础实现逐步扩展:
DFS
↓
HashMap
↓
File Content
↓
Large File
↓
Streaming Hash
↓
Size Pre-filter
↓
Hash Collision
↓
Error Handling
↓
Large-scale Optimization
这种提问方式使得基础算法只是起点,后续 Follow-up 更关注候选人是否能够将一个简单算法转化为真实工程方案。
面试官的具体职级在材料中没有得到确认,因此不将其职级作为确定事实。
📚 Preparation
针对这类工程型 Coding 面试,可以重点准备:
- DFS / Recursive Directory Traversal
- HashMap Grouping
- File I/O
- Streaming / Chunk-based Processing
- Hash Function
- Hash Collision
- Exception Handling
- Large-scale Data Processing
- Trade-off Analysis
同时可以练习 Parking Lot、File System、Elevator System、Shopping Cart & Promotion Engine 等 OOD/工程型题目,训练从基础实现逐步扩展到工程设计的能力。
💡 My Takeaways
这道题表面上是 HashMap + DFS,真正拉开差距的是工程细节。
遇到文件去重问题时,第一反应应该是:
遍历文件
→
生成内容 Signature
→
HashMap 分组
基础方案完成后,再主动升级:
Size Pre-filter
→
Streaming Hash
→
Hash Collision Verification
→
Error Handling
→
Large-scale Optimization
尤其需要注意,题目名称、示例输出和实际需求可能存在差异。例如题目叫 Duplicate Files,但示例中又出现了单独的 unique file,因此应该在一开始主动确认输出定义。
对于工程型面试来说,代码能够运行只是第一层。能够解释为什么这么设计、哪里可能出现问题、规模变大以后怎么优化,以及不同方案之间有什么 Trade-off,才是进一步体现工程能力的关键。
General Advice
面对类似题目,可以固定使用以下表达结构:
1. Restate the problem
2. Clarify ambiguous requirements
3. Explain the basic solution
4. Implement the core logic
5. Analyze time and space complexity
6. Discuss edge cases
7. Proactively discuss engineering optimizations
如果面试官继续追问,可以沿着:
Correctness
→
Performance
→
Memory
→
Scalability
→
Reliability
逐层展开。
📝 Interview Experience Summary
这场面试表面上是一道文件去重题,核心并不是简单写一个 HashMap。
题目从一个 root path 开始,需要递归找到所有文件,再按照文件内容进行分组。真正容易踩坑的是,不能只看文件名,也不能默认所有文件都很小。
比较完整的工程方案应该是先遍历文件,再按照文件大小进行预筛选,然后对同大小文件使用 streaming hash。对于 hash 相同的文件,如果业务对准确性要求较高,还可以进一步进行 byte-by-byte verification。
另外,题目中的输出定义也值得注意。示例中出现了 unique file,因此面试开始时应该主动确认到底需要返回所有分组,还是只返回真正存在重复文件的分组。
这类题的核心思维可以总结成一句话:
把每个文件映射成一个内容 Signature,再按照 Signature 进行分组。
真正的工程升级则是:
Size Pre-filter + Streaming Hash + Collision Verification + Error Handling。
Categories:
Software Engineering, Algorithm
